From ddde023c8ec2611a34fdd04f7465705125e2576f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 23 Jul 2022 04:20:37 -0700 Subject: [PATCH 001/261] Abstract parent copy does not suspend case copy --- .../scala/tools/nsc/typechecker/Namers.scala | 2 +- test/files/pos/t12623.scala | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t12623.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 8236d9cb7b0a..bde0dbde848c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1210,7 +1210,7 @@ trait Namers extends MethodSynthesis { val modClass = companionSymbolOf(clazz, context).moduleClass modClass.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass - def hasCopy = (decls containsName nme.copy) || parents.exists(_.member(nme.copy).exists) + def hasCopy = decls.containsName(nme.copy) || parents.exists { p => val ov = p.member(nme.copy); ov.exists && !ov.isDeferred } // scala/bug#5956 needs (cdef.symbol == clazz): there can be multiple class symbols with the same name if (cdef.symbol == clazz && !hasCopy) diff --git a/test/files/pos/t12623.scala b/test/files/pos/t12623.scala new file mode 100644 index 000000000000..a51be649dcec --- /dev/null +++ b/test/files/pos/t12623.scala @@ -0,0 +1,19 @@ + +trait MapI[C] { + def i: Int + def s: String + def copy(i: Int = this.i, s: String = this.s): C + def mapI(i: Int): C = copy(i) +} + +case class C(i: Int, s: String) extends MapI[C] + +/* +was: +t12623.scala:9: error: class C needs to be abstract. +Missing implementation for member of trait MapI: + def copy(i: Int, s: String): C = ??? + +case class C(i: Int, s: String) extends MapI[C] + ^ + */ From 4fd868ef7ac40f6c5c070410fd70c5611cee9543 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 18 Aug 2022 03:12:17 -0700 Subject: [PATCH 002/261] Fix Xlint:eta-sam description --- src/compiler/scala/tools/nsc/settings/Warnings.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index b5d12f83683a..7a059af9155f 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -205,7 +205,7 @@ trait Warnings { val Serial = LintWarning("serial", "@SerialVersionUID on traits and non-serializable classes.") val ValPattern = LintWarning("valpattern", "Enable pattern checks in val definitions.") val EtaZero = LintWarning("eta-zero", "Usage `f` of parameterless `def f()` resulted in eta-expansion, not empty application `f()`.") - val EtaSam = LintWarning("eta-sam", "The Java-defined target interface for eta-expansion was not annotated @FunctionalInterface.") + val EtaSam = LintWarning("eta-sam", "A method reference was eta-expanded but the expected SAM type was not annotated @FunctionalInterface.") val Deprecation = LintWarning("deprecation", "Enable -deprecation and also check @deprecated annotations.") val ByNameImplicit = LintWarning("byname-implicit", "Block adapted by implicit with by-name parameter.") val RecurseWithDefault = LintWarning("recurse-with-default", "Recursive call used default argument.") From 08ff418c52104a5ad33f6f041a8bbfbb61d4f071 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 18 Aug 2022 14:51:25 -0700 Subject: [PATCH 003/261] Warn eta-sam under -Xsource:3 per Scala 3 --- .../scala/tools/nsc/typechecker/Typers.scala | 43 ++++++++++--------- test/files/neg/t11644a.check | 17 ++++++++ test/files/neg/t11644a.scala | 26 +++++++++++ test/files/neg/t11644b.check | 21 +++++++++ test/files/neg/t11644b.scala | 24 +++++++++++ test/files/neg/t7187-3.check | 4 +- test/files/neg/t7187-3.scala | 4 +- test/files/neg/t7187-deprecation.check | 4 +- test/files/neg/t7187-deprecation.scala | 4 +- test/files/neg/t7187.scala | 2 +- 10 files changed, 119 insertions(+), 30 deletions(-) create mode 100644 test/files/neg/t11644a.check create mode 100644 test/files/neg/t11644a.scala create mode 100644 test/files/neg/t11644b.check create mode 100644 test/files/neg/t11644b.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 4e9a7fb8ece5..85ff57be7720 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -884,28 +884,29 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def warnTree = original orElse tree - def warnEtaZero(): Boolean = { - if (!settings.warnEtaZero) return true - context.warning(tree.pos, - s"""An unapplied 0-arity method was eta-expanded (due to the expected type $pt), rather than applied to `()`. - |Write ${Apply(warnTree, Nil)} to invoke method ${meth.decodedName}, or change the expected type.""".stripMargin, - WarningCategory.LintEtaZero) - true - } - - def warnEtaSam(): Boolean = { - if (!settings.warnEtaSam) return true - val sam = samOf(pt) - val samClazz = sam.owner - // TODO: we allow a Java class as a SAM type, whereas Java only allows the @FunctionalInterface on interfaces -- align? - if (sam.exists && (!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) + def warnEtaZero(): true = + if (!settings.warnEtaZero) true + else { context.warning(tree.pos, - s"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)}, - |even though $samClazz is not annotated with `@FunctionalInterface`; - |to suppress warning, add the annotation or write out the equivalent function literal.""".stripMargin, - WarningCategory.LintEtaSam) - true - } + s"""An unapplied 0-arity method was eta-expanded (due to the expected type $pt), rather than applied to `()`. + |Write ${Apply(warnTree, Nil)} to invoke method ${meth.decodedName}, or change the expected type.""".stripMargin, + WarningCategory.LintEtaZero) + true + } + + def warnEtaSam(): true = + if (!settings.warnEtaSam && !currentRun.isScala3) true + else { + val sam = samOf(pt) + val samClazz = sam.owner + if (sam.exists && (!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) + context.warning(tree.pos, + s"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)}, + |even though $samClazz is not annotated with `@FunctionalInterface`; + |to suppress warning, add the annotation or write out the equivalent function literal.""".stripMargin, + WarningCategory.LintEtaSam) + true + } // note that isFunctionProto(pt) does not work properly for Function0 lazy val ptUnderlying = diff --git a/test/files/neg/t11644a.check b/test/files/neg/t11644a.check new file mode 100644 index 000000000000..2a07740dc181 --- /dev/null +++ b/test/files/neg/t11644a.check @@ -0,0 +1,17 @@ +t11644a.scala:20: error: type mismatch; + found : Int + required: AcciSamZero + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + ^ +t11644a.scala:21: error: type mismatch; + found : Int + required: SamZero + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types + ^ +t11644a.scala:24: warning: Eta-expansion performed to meet expected type AcciSamOne, which is SAM-equivalent to Int => Int, +even though trait AcciSamOne is not annotated with `@FunctionalInterface`; +to suppress warning, add the annotation or write out the equivalent function literal. + val t3AcciSam: AcciSamOne = m3 // warn + ^ +1 warning +2 errors diff --git a/test/files/neg/t11644a.scala b/test/files/neg/t11644a.scala new file mode 100644 index 000000000000..1dfe0f55da12 --- /dev/null +++ b/test/files/neg/t11644a.scala @@ -0,0 +1,26 @@ +// scalac: -Xsource:3 +// +// eta-expansion to SAM type always warns in Scala 3 world + +trait AcciSamZero { def apply(): Int } + +@FunctionalInterface +trait SamZero { def apply(): Int } + +trait AcciSamOne { def apply(i: Int): Int } + +@FunctionalInterface +trait SamOne { def apply(i: Int): Int } + +class EtaExpand214 { + def m2() = 1 + def m3(x: Int) = x + + val t2: () => Any = m2 // eta-expanded with lint warning + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types + + val t3: Int => Any = m3 // no warn + val t3AcciSam: AcciSamOne = m3 // warn + val t3Sam: SamOne = m3 // no warn +} diff --git a/test/files/neg/t11644b.check b/test/files/neg/t11644b.check new file mode 100644 index 000000000000..ca1252f0eac7 --- /dev/null +++ b/test/files/neg/t11644b.check @@ -0,0 +1,21 @@ +t11644b.scala:18: error: type mismatch; + found : Int + required: AcciSamZero + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + ^ +t11644b.scala:19: error: type mismatch; + found : Int + required: SamZero + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + ^ +t11644b.scala:17: warning: An unapplied 0-arity method was eta-expanded (due to the expected type () => Any), rather than applied to `()`. +Write m2() to invoke method m2, or change the expected type. + val t2: () => Any = m2 // eta-expanded with lint warning + ^ +t11644b.scala:22: warning: Eta-expansion performed to meet expected type AcciSamOne, which is SAM-equivalent to Int => Int, +even though trait AcciSamOne is not annotated with `@FunctionalInterface`; +to suppress warning, add the annotation or write out the equivalent function literal. + val t3AcciSam: AcciSamOne = m3 // warn + ^ +2 warnings +2 errors diff --git a/test/files/neg/t11644b.scala b/test/files/neg/t11644b.scala new file mode 100644 index 000000000000..299667389a50 --- /dev/null +++ b/test/files/neg/t11644b.scala @@ -0,0 +1,24 @@ +// scalac: -Xlint:deprecation,eta-zero,eta-sam + +trait AcciSamZero { def apply(): Int } + +@FunctionalInterface +trait SamZero { def apply(): Int } + +trait AcciSamOne { def apply(i: Int): Int } + +@FunctionalInterface +trait SamOne { def apply(i: Int): Int } + +class EtaExpand214 { + def m2() = 1 + def m3(x: Int) = x + + val t2: () => Any = m2 // eta-expanded with lint warning + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + + val t3: Int => Any = m3 // no warn + val t3AcciSam: AcciSamOne = m3 // warn + val t3Sam: SamOne = m3 // no warn +} diff --git a/test/files/neg/t7187-3.check b/test/files/neg/t7187-3.check index 6ea263f0f35b..ba739c55898b 100644 --- a/test/files/neg/t7187-3.check +++ b/test/files/neg/t7187-3.check @@ -6,12 +6,12 @@ t7187-3.scala:13: error: type mismatch; t7187-3.scala:15: error: type mismatch; found : Int required: AcciSamZero - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-3.scala:16: error: type mismatch; found : Int required: SamZero - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-3.scala:27: error: Methods without a parameter list and by-name params can not be converted to functions as `m _`, write a function literal `() => m` instead val t7 = m1 _ // error: eta-expanding a nullary method diff --git a/test/files/neg/t7187-3.scala b/test/files/neg/t7187-3.scala index 9216d09c579f..a3bc502ba6b1 100644 --- a/test/files/neg/t7187-3.scala +++ b/test/files/neg/t7187-3.scala @@ -12,8 +12,8 @@ class EtaExpand214 { val t1: () => Any = m1 // error val t2: () => Any = m2 // eta-expanded with lint warning - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types val t3: Int => Any = m3 // ok val t4 = m1 // apply diff --git a/test/files/neg/t7187-deprecation.check b/test/files/neg/t7187-deprecation.check index edbc44fe2b11..ae8772e4352e 100644 --- a/test/files/neg/t7187-deprecation.check +++ b/test/files/neg/t7187-deprecation.check @@ -6,12 +6,12 @@ t7187-deprecation.scala:17: error: type mismatch; t7187-deprecation.scala:19: error: type mismatch; found : Int required: AcciSamZero - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-deprecation.scala:20: error: type mismatch; found : Int required: SamZero - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ t7187-deprecation.scala:31: error: Methods without a parameter list and by-name params can not be converted to functions as `m _`, write a function literal `() => m` instead val t7 = m1 _ // error: eta-expanding a nullary method diff --git a/test/files/neg/t7187-deprecation.scala b/test/files/neg/t7187-deprecation.scala index 156642ff287b..7808df726e59 100644 --- a/test/files/neg/t7187-deprecation.scala +++ b/test/files/neg/t7187-deprecation.scala @@ -16,8 +16,8 @@ class EtaExpand214 { val t1: () => Any = m1 // error val t2: () => Any = m2 // eta-expanded, only warns w/ -Xlint:eta-zero - val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 - val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types under -Xsource:3 + val t2AcciSam: AcciSamZero = m2 // error, nilary methods don't eta-expand to SAM types + val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types val t3: Int => Any = m3 // ok val t4 = m1 // apply diff --git a/test/files/neg/t7187.scala b/test/files/neg/t7187.scala index 69fd0d3ee487..f58d7e49cb98 100644 --- a/test/files/neg/t7187.scala +++ b/test/files/neg/t7187.scala @@ -1,4 +1,4 @@ -// scalac: -deprecation -Xlint:eta-zero -Xlint:eta-sam +// scalac: -Xlint:deprecation,eta-zero,eta-sam // trait AcciSamOne { def apply(x: Int): Int } From 61ab759126a77c3b0fc18fa9dc78065c717bc3d3 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 18 Aug 2022 15:36:12 -0700 Subject: [PATCH 004/261] Use strip margin interpolator by import or package --- src/compiler/scala/reflect/reify/Errors.scala | 8 +++++--- src/compiler/scala/tools/nsc/package.scala | 6 ++++++ .../transform/patmat/MatchTranslation.scala | 3 ++- .../scala/tools/nsc/typechecker/Typers.scala | 6 +++--- .../scala/reflect/internal/Definitions.scala | 1 + .../scala/reflect/internal/SymbolPairs.scala | 2 +- .../scala/reflect/internal/SymbolTable.scala | 6 ------ .../scala/reflect/internal/Trees.scala | 2 +- .../reflect/internal/tpe/TypeComparers.scala | 4 ++-- .../scala/reflect/internal/tpe/TypeMaps.scala | 5 +++-- .../scala/reflect/runtime/JavaMirrors.scala | 20 +++++++++---------- 11 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 012eca623c19..215633708961 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -10,10 +10,12 @@ * additional information regarding copyright ownership. */ -package scala.reflect.reify +package scala.reflect +package reify -import scala.reflect.macros.ReificationException -import scala.reflect.macros.UnexpectedReificationException +import internal.util.StringContextStripMarginOps +import macros.ReificationException +import macros.UnexpectedReificationException trait Errors { self: Reifier => diff --git a/src/compiler/scala/tools/nsc/package.scala b/src/compiler/scala/tools/nsc/package.scala index 46cd59b63625..10c2a9ac9af7 100644 --- a/src/compiler/scala/tools/nsc/package.scala +++ b/src/compiler/scala/tools/nsc/package.scala @@ -12,6 +12,8 @@ package scala.tools +import scala.reflect.internal.util.StringContextStripMarginOps + package object nsc { type Mode = scala.reflect.internal.Mode val Mode = scala.reflect.internal.Mode @@ -32,4 +34,8 @@ package object nsc { @deprecated("Use scala.reflect.internal.util.ListOfNil", "2.11.0") lazy val ListOfNil = scala.reflect.internal.util.ListOfNil + + /** Adds the `sm` interpolator to a [[scala.StringContext]]. + */ + implicit val `strip margin`: StringContext => StringContextStripMarginOps = StringContextStripMarginOps } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index 14f7f12e5027..d88456829b73 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -10,7 +10,8 @@ * additional information regarding copyright ownership. */ -package scala.tools.nsc.transform.patmat +package scala.tools.nsc +package transform.patmat /** Translate typed Trees that represent pattern matches into the patternmatching IR, defined by TreeMakers. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 85ff57be7720..3f3693e39030 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -17,7 +17,7 @@ package typechecker import scala.annotation.tailrec import scala.collection.mutable import scala.reflect.internal.{Chars, TypesStats} -import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics} +import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps} import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory} import scala.util.chaining._ import mutable.ListBuffer @@ -901,9 +901,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val samClazz = sam.owner if (sam.exists && (!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) context.warning(tree.pos, - s"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)}, + sm"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)}, |even though $samClazz is not annotated with `@FunctionalInterface`; - |to suppress warning, add the annotation or write out the equivalent function literal.""".stripMargin, + |to suppress warning, add the annotation or write out the equivalent function literal.""", WarningCategory.LintEtaSam) true } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index ef7a73c87258..dff8796330b0 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -19,6 +19,7 @@ import scala.collection.mutable import Flags._ import scala.reflect.api.{Universe => ApiUniverse} import PartialFunction.cond +import util.StringContextStripMarginOps trait Definitions extends api.StandardDefinitions { self: SymbolTable => diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala index 7d2f1f895550..824fc1acb766 100644 --- a/src/reflect/scala/reflect/internal/SymbolPairs.scala +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -14,7 +14,7 @@ package scala package reflect package internal -import util.HashSet +import util.{HashSet, StringContextStripMarginOps} import scala.annotation.tailrec /** An abstraction for considering symbol pairs. diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index f0fcae031058..c67fe257bf60 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -509,12 +509,6 @@ abstract class SymbolTable extends macros.Universe @deprecated("use enteringPhase", "2.10.0") // Used in sbt 0.12.4 @inline final def atPhase[T](ph: Phase)(op: => T): T = enteringPhase(ph)(op) - - /** - * Adds the `sm` String interpolator to a [[scala.StringContext]]. - */ - implicit val StringContextStripMarginOps: StringContext => StringContextStripMarginOps = util.StringContextStripMarginOps - protected[scala] def currentRunProfilerBeforeCompletion(root: Symbol, associatedFile: AbstractFile): Unit = () protected[scala] def currentRunProfilerAfterCompletion(root: Symbol, associatedFile: AbstractFile): Unit = () } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 5a07f3ccd75c..0217da48700c 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -19,7 +19,7 @@ import scala.annotation.{nowarn, tailrec} import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.reflect.macros.Attachments -import util.{ReusableInstance, Statistics} +import util.{ReusableInstance, Statistics, StringContextStripMarginOps} trait Trees extends api.Trees { self: SymbolTable => diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index 92357d0e0e19..414da1c386f1 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -15,9 +15,9 @@ package reflect package internal package tpe -import scala.collection.mutable -import util.TriState import scala.annotation.tailrec +import scala.collection.mutable +import util.{StringContextStripMarginOps, TriState} trait TypeComparers { self: SymbolTable => diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 96684ffe9f3e..a8997fa04486 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -15,11 +15,12 @@ package reflect package internal package tpe +import scala.annotation.{nowarn, tailrec} import scala.collection.{immutable, mutable} +import scala.collection.mutable.ListBuffer import Flags._ -import scala.annotation.{nowarn, tailrec} import Variance._ -import scala.collection.mutable.ListBuffer +import util.StringContextStripMarginOps private[internal] trait TypeMaps { self: SymbolTable => diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index e468c68f8697..505a4b02fc97 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -14,12 +14,6 @@ package scala package reflect package runtime -import scala.language.existentials - -import scala.ref.WeakReference -import scala.collection.mutable.WeakHashMap -import scala.collection.immutable.ArraySeq - import java.io.IOException import java.lang.{ Class => jClass, Package => jPackage } import java.lang.annotation.{ Annotation => jAnnotation } @@ -31,15 +25,19 @@ import java.lang.reflect.{ ParameterizedType, WildcardType, AnnotatedElement } import java.nio.charset.StandardCharsets.UTF_8 +import scala.annotation.nowarn +import scala.collection.immutable.ArraySeq +import scala.collection.mutable.{ListBuffer, WeakHashMap} +import scala.language.existentials +import scala.ref.WeakReference +import scala.reflect.api.TypeCreator import scala.reflect.internal.{ JavaAccFlags, MissingRequirementError } +import scala.runtime.{BoxesRunTime, ClassValueCompat, ScalaRunTime} +import internal.Flags._ import internal.pickling.ByteCodecs import internal.pickling.UnPickler -import scala.collection.mutable.ListBuffer -import internal.Flags._ +import internal.util.StringContextStripMarginOps import ReflectionUtils._ -import scala.annotation.nowarn -import scala.reflect.api.TypeCreator -import scala.runtime.{BoxesRunTime, ClassValueCompat, ScalaRunTime} private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable => From bffe42aa59b519addeb210377aadc576242e0b7f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 19 Aug 2022 03:15:22 -0700 Subject: [PATCH 005/261] Massage message for eta-expand functional interface --- .../scala/tools/nsc/typechecker/Typers.scala | 21 ++++++++++++------- test/files/neg/t11644a.check | 7 ++++--- test/files/neg/t11644a.scala | 2 ++ test/files/neg/t11644b.check | 7 ++++--- test/files/neg/t7187.check | 7 ++++--- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3f3693e39030..eaeac77a94ac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -898,13 +898,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!settings.warnEtaSam && !currentRun.isScala3) true else { val sam = samOf(pt) - val samClazz = sam.owner - if (sam.exists && (!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) - context.warning(tree.pos, - sm"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)}, - |even though $samClazz is not annotated with `@FunctionalInterface`; - |to suppress warning, add the annotation or write out the equivalent function literal.""", - WarningCategory.LintEtaSam) + if (sam.exists) { + val samClazz = sam.owner + if ((!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) { + val ft = samToFunctionType(pt) + val sample = Function(meth.paramss.head.map(ValDef(_)), Apply(meth, meth.paramss.head.map(p => Ident(p.name)): _*)) + val places = Apply(meth, meth.paramss.head.map(_ => Ident(nme.USCOREkw)): _*) + context.warning(tree.pos, + sm"""Eta-expansion to expected type $pt, which is not a function type but is SAM-convertible to $ft. + |$samClazz should be annotated with `@FunctionalInterface` if eta-expansion is desired. + |Or, avoid eta-expansion by writing the function literal `$sample` or `$places`. + |This warning can be filtered with `-Wconf:cat=lint-eta-sam`.""", + WarningCategory.LintEtaSam) + } + } true } diff --git a/test/files/neg/t11644a.check b/test/files/neg/t11644a.check index 2a07740dc181..594d3236643e 100644 --- a/test/files/neg/t11644a.check +++ b/test/files/neg/t11644a.check @@ -8,9 +8,10 @@ t11644a.scala:21: error: type mismatch; required: SamZero val t2Sam: SamZero = m2 // error, nilary methods don't eta-expand to SAM types ^ -t11644a.scala:24: warning: Eta-expansion performed to meet expected type AcciSamOne, which is SAM-equivalent to Int => Int, -even though trait AcciSamOne is not annotated with `@FunctionalInterface`; -to suppress warning, add the annotation or write out the equivalent function literal. +t11644a.scala:24: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. +trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Or, avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t3AcciSam: AcciSamOne = m3 // warn ^ 1 warning diff --git a/test/files/neg/t11644a.scala b/test/files/neg/t11644a.scala index 1dfe0f55da12..3e9ef0dcb0bb 100644 --- a/test/files/neg/t11644a.scala +++ b/test/files/neg/t11644a.scala @@ -22,5 +22,7 @@ class EtaExpand214 { val t3: Int => Any = m3 // no warn val t3AcciSam: AcciSamOne = m3 // warn + val t3SamLit: AcciSamOne = (x: Int) => m3(x) // no warn + val t3SamPH: AcciSamOne = m3(_) // no warn val t3Sam: SamOne = m3 // no warn } diff --git a/test/files/neg/t11644b.check b/test/files/neg/t11644b.check index ca1252f0eac7..3607e43d37a1 100644 --- a/test/files/neg/t11644b.check +++ b/test/files/neg/t11644b.check @@ -12,9 +12,10 @@ t11644b.scala:17: warning: An unapplied 0-arity method was eta-expanded (due to Write m2() to invoke method m2, or change the expected type. val t2: () => Any = m2 // eta-expanded with lint warning ^ -t11644b.scala:22: warning: Eta-expansion performed to meet expected type AcciSamOne, which is SAM-equivalent to Int => Int, -even though trait AcciSamOne is not annotated with `@FunctionalInterface`; -to suppress warning, add the annotation or write out the equivalent function literal. +t11644b.scala:22: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. +trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Or, avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t3AcciSam: AcciSamOne = m3 // warn ^ 2 warnings diff --git a/test/files/neg/t7187.check b/test/files/neg/t7187.check index e81eb7aac0cd..cc1e7b3ea31d 100644 --- a/test/files/neg/t7187.check +++ b/test/files/neg/t7187.check @@ -49,9 +49,10 @@ t7187.scala:33: warning: An unapplied 0-arity method was eta-expanded (due to th Write zap()() to invoke method zap, or change the expected type. val t4b: () => Any = zap() // ditto ^ -t7187.scala:40: warning: Eta-expansion performed to meet expected type AcciSamOne, which is SAM-equivalent to Int => Int, -even though trait AcciSamOne is not annotated with `@FunctionalInterface`; -to suppress warning, add the annotation or write out the equivalent function literal. +t7187.scala:40: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. +trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Or, avoid eta-expansion by writing the function literal `((x: Int) => zup(x))` or `zup(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t5AcciSam: AcciSamOne = zup // ok, but warning ^ 8 warnings From cbb9e93bf5633631f6f0c4f2e4cb61f3f3ca9ddc Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 19 Aug 2022 03:33:58 -0700 Subject: [PATCH 006/261] Warn eta-expand to Java class with tweaked advice --- .../scala/tools/nsc/typechecker/Typers.scala | 27 ++++++++++--------- test/files/neg/t11644a.check | 2 +- test/files/neg/t11644b.check | 2 +- test/files/neg/t11644c.check | 14 ++++++++++ test/files/neg/t11644c/J.java | 7 +++++ test/files/neg/t11644c/s.scala | 16 +++++++++++ test/files/neg/t7187.check | 2 +- 7 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 test/files/neg/t11644c.check create mode 100644 test/files/neg/t11644c/J.java create mode 100644 test/files/neg/t11644c/s.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index eaeac77a94ac..7fa9ba2e0d39 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -884,36 +884,37 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def warnTree = original orElse tree - def warnEtaZero(): true = - if (!settings.warnEtaZero) true - else { + def warnEtaZero(): true = { + if (settings.warnEtaZero) { context.warning(tree.pos, s"""An unapplied 0-arity method was eta-expanded (due to the expected type $pt), rather than applied to `()`. |Write ${Apply(warnTree, Nil)} to invoke method ${meth.decodedName}, or change the expected type.""".stripMargin, WarningCategory.LintEtaZero) - true } + true + } - def warnEtaSam(): true = - if (!settings.warnEtaSam && !currentRun.isScala3) true - else { + def warnEtaSam(): true = { + if (settings.warnEtaSam || currentRun.isScala3) { val sam = samOf(pt) if (sam.exists) { val samClazz = sam.owner - if ((!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) { + val isJavaClass = samClazz.isJava && !samClazz.isInterface + if (!samClazz.hasAnnotation(definitions.FunctionalInterfaceClass)) { val ft = samToFunctionType(pt) val sample = Function(meth.paramss.head.map(ValDef(_)), Apply(meth, meth.paramss.head.map(p => Ident(p.name)): _*)) val places = Apply(meth, meth.paramss.head.map(_ => Ident(nme.USCOREkw)): _*) + val advice = if (isJavaClass) "" else s"\n$samClazz should be annotated with `@FunctionalInterface` if eta-expansion is desired." context.warning(tree.pos, - sm"""Eta-expansion to expected type $pt, which is not a function type but is SAM-convertible to $ft. - |$samClazz should be annotated with `@FunctionalInterface` if eta-expansion is desired. - |Or, avoid eta-expansion by writing the function literal `$sample` or `$places`. - |This warning can be filtered with `-Wconf:cat=lint-eta-sam`.""", + sm"""Eta-expansion to expected type $pt, which is not a function type but is SAM-convertible to $ft.$advice + |Avoid eta-expansion by writing the function literal `$sample` or `$places`. + |This warning can be filtered with `-Wconf:cat=lint-eta-sam`.""", WarningCategory.LintEtaSam) } } - true } + true + } // note that isFunctionProto(pt) does not work properly for Function0 lazy val ptUnderlying = diff --git a/test/files/neg/t11644a.check b/test/files/neg/t11644a.check index 594d3236643e..6cee9521f5d1 100644 --- a/test/files/neg/t11644a.check +++ b/test/files/neg/t11644a.check @@ -10,7 +10,7 @@ t11644a.scala:21: error: type mismatch; ^ t11644a.scala:24: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. -Or, avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. +Avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t3AcciSam: AcciSamOne = m3 // warn ^ diff --git a/test/files/neg/t11644b.check b/test/files/neg/t11644b.check index 3607e43d37a1..c24d848a4208 100644 --- a/test/files/neg/t11644b.check +++ b/test/files/neg/t11644b.check @@ -14,7 +14,7 @@ Write m2() to invoke method m2, or change the expected type. ^ t11644b.scala:22: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. -Or, avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. +Avoid eta-expansion by writing the function literal `((x: Int) => m3(x))` or `m3(_)`. This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t3AcciSam: AcciSamOne = m3 // warn ^ diff --git a/test/files/neg/t11644c.check b/test/files/neg/t11644c.check new file mode 100644 index 000000000000..7f1c788df184 --- /dev/null +++ b/test/files/neg/t11644c.check @@ -0,0 +1,14 @@ +s.scala:13: warning: Eta-expansion to expected type J, which is not a function type but is SAM-convertible to Int => Int. +Avoid eta-expansion by writing the function literal `((i: Int) => bump(i))` or `bump(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. + c.f(bump), + ^ +s.scala:14: warning: Eta-expansion to expected type K, which is not a function type but is SAM-convertible to Int => Int. +trait K should be annotated with `@FunctionalInterface` if eta-expansion is desired. +Avoid eta-expansion by writing the function literal `((i: Int) => bump(i))` or `bump(_)`. +This warning can be filtered with `-Wconf:cat=lint-eta-sam`. + c.g(bump), + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t11644c/J.java b/test/files/neg/t11644c/J.java new file mode 100644 index 000000000000..95a3612859da --- /dev/null +++ b/test/files/neg/t11644c/J.java @@ -0,0 +1,7 @@ + +public abstract class J { + public abstract int f(int i); +} +interface K { + int f(int i); +} diff --git a/test/files/neg/t11644c/s.scala b/test/files/neg/t11644c/s.scala new file mode 100644 index 000000000000..185ba67ad221 --- /dev/null +++ b/test/files/neg/t11644c/s.scala @@ -0,0 +1,16 @@ +// scalac: -Xsource:3 -Werror + +class C { + def f(j: J): Int = j.f(42) + def g(k: K): Int = k.f(17) +} +object Test extends App { + def bump(i: Int): Int = i + 1 + val c = new C + println {( + c.f((i: Int) => i + 1), + c.g((i: Int) => i + 1), + c.f(bump), + c.g(bump), + )} +} diff --git a/test/files/neg/t7187.check b/test/files/neg/t7187.check index cc1e7b3ea31d..696b7bbbaa4f 100644 --- a/test/files/neg/t7187.check +++ b/test/files/neg/t7187.check @@ -51,7 +51,7 @@ Write zap()() to invoke method zap, or change the expected type. ^ t7187.scala:40: warning: Eta-expansion to expected type AcciSamOne, which is not a function type but is SAM-convertible to Int => Int. trait AcciSamOne should be annotated with `@FunctionalInterface` if eta-expansion is desired. -Or, avoid eta-expansion by writing the function literal `((x: Int) => zup(x))` or `zup(_)`. +Avoid eta-expansion by writing the function literal `((x: Int) => zup(x))` or `zup(_)`. This warning can be filtered with `-Wconf:cat=lint-eta-sam`. val t5AcciSam: AcciSamOne = zup // ok, but warning ^ From 07713294e58fe8882535b63e243ecd76cb280756 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 26 Aug 2022 08:11:12 -0700 Subject: [PATCH 007/261] Accessible lint less triggered for sealed, restricted owners --- .../tools/nsc/typechecker/RefChecks.scala | 9 +++---- .../scala/reflect/internal/Symbols.scala | 9 +++---- test/files/pos/t9490.scala | 27 +++++++++++++++++++ 3 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 test/files/pos/t9490.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 2b455e38e17f..c8d9651d819e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1323,15 +1323,14 @@ abstract class RefChecks extends Transform { && !otherSym.isProtected && !otherSym.isTypeParameterOrSkolem && !otherSym.isExistentiallyBound - && (otherSym isLessAccessibleThan memberSym) - && (otherSym isLessAccessibleThan memberSym.enclClass) + && memberSym.ownersIterator.forall(otherSym.isLessAccessibleThan(_)) ) private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = { val extras = other match { case TypeRef(pre, _, args) => // checking the prefix here gives us spurious errors on e.g. a private[process] // object which contains a type alias, which normalizes to a visible type. - args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym)) + args.filterNot(_ eq NoPrefix).flatMap(lessAccessibleSymsInType(_, memberSym)) case _ => Nil } @@ -1367,7 +1366,7 @@ abstract class RefChecks extends Transform { // or if the normalized type is, that's good too else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) () // otherwise warn about the inaccessible syms in the unnormalized type - else inaccessible foreach (sym => warnLessAccessible(sym, member)) + else inaccessible.foreach(warnLessAccessible(_, member)) } // types of the value parameters @@ -1823,7 +1822,7 @@ abstract class RefChecks extends Transform { if (settings.warnNullaryUnit) checkNullaryMethodReturnType(sym) if (settings.warnInaccessible) { - if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.isSynthetic) + if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.owner.isSealed && !sym.isSynthetic) checkAccessibilityOfReferencedTypes(tree) } tree match { diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index cffd4c8f1dcb..a41fbe6c9be4 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1012,18 +1012,17 @@ trait Symbols extends api.Symbols { self: SymbolTable => isPackageClass || isModuleClass && isStatic /** A helper function for isEffectivelyFinal. */ - private def isNotOverridden = ( + private def isNotOverridden = owner.isClass && ( owner.isEffectivelyFinal - || (owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && (overridingSymbol(c) == NoSymbol))) + || owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && overridingSymbol(c) == NoSymbol) ) - ) /** Is this symbol effectively final? I.e, it cannot be overridden */ final def isEffectivelyFinal: Boolean = ( - ((this hasFlag FINAL | PACKAGE) && this != SingletonClass) + hasFlag(FINAL | PACKAGE) && this != SingletonClass || isModuleOrModuleClass - || isTerm && (isPrivate || isLocalToBlock || (hasAllFlags(notPRIVATE | METHOD) && !hasFlag(DEFERRED))) + || isTerm && (isPrivate || isLocalToBlock || hasAllFlags(notPRIVATE | METHOD) && !hasFlag(DEFERRED)) // We track known subclasses of term-owned classes, use that to infer finality. // However, don't look at owner for refinement classes (it's basically arbitrary). || isClass && !isRefinementClass && originalOwner.isTerm && children.isEmpty diff --git a/test/files/pos/t9490.scala b/test/files/pos/t9490.scala new file mode 100644 index 000000000000..e874724d092b --- /dev/null +++ b/test/files/pos/t9490.scala @@ -0,0 +1,27 @@ +// scalac: -Werror -Xlint:inaccessible + +package ws { + private[ws] trait Foo + private[ws] object Test { + class Bar { + def apply(f: Foo) = ??? + } + } +} + +package p { + private[p] class D + sealed trait T { def f(d: D): Unit } + final class C extends T { def f(d: D) = () } +} + +/* was: +t9490.scala:7: warning: method apply in class Bar references private[ws] trait Foo. +Classes which cannot access Foo may be unable to override apply. + def apply(f: Foo) = ??? + ^ +t9490.scala:14: warning: method f in trait T references private[p] class D. +Classes which cannot access D may be unable to provide a concrete implementation of f. + sealed trait T { def f(d: D): Unit } + ^ + */ From ba1791605006fbbd269377dee15e9a3170d0477d Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 31 Aug 2022 17:16:40 +0200 Subject: [PATCH 008/261] don't discard undetparams of child context when typing a block --- .../scala/tools/nsc/typechecker/Typers.scala | 5 ++++- .../tools/nsc/typechecker/TypedTreeTest.scala | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e1eed2b88345..8432f9e2e193 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5963,7 +5963,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // Trees not allowed during pattern mode. def typedOutsidePatternMode(tree: Tree): Tree = tree match { - case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner))(_.typedBlock(tree, mode, pt)) + case tree: Block => + val blockContext = context.makeNewScope(tree, context.owner) + try typerWithLocalContext(blockContext)(_.typedBlock(tree, mode, pt)) + finally context.undetparams ++= blockContext.undetparams case tree: If => typedIf(tree) case tree: TypeApply => typedTypeApply(tree) case tree: Function => typedFunction(tree) diff --git a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala index 0c0a6b96f286..fe798a7de36e 100644 --- a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala +++ b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala @@ -8,6 +8,23 @@ import scala.tools.testkit.BytecodeTesting class TypedTreeTest extends BytecodeTesting { override def compilerArgs = "-Ystop-after:typer" + @Test + def keepBlockUndetparams(): Unit = { + import compiler.global._ + val code = + """class C { + | def f = Option(Map("a" -> "c")).getOrElse { println(); Map.empty } // was: Map[_ >: String with K, Any] + | def g = Option(Map("a" -> "c")).getOrElse { Map.empty } + |} + |""".stripMargin + val run = compiler.newRun() + run.compileSources(List(BytecodeTesting.makeSourceFile(code, "UnitTestSource.scala"))) + val t: Tree = run.units.next().body + val c: Symbol = t.collect { case cd: ClassDef => cd.symbol }.head + for (m <- List("f", "g")) + assertEquals(c.info.member(TermName("f")).tpe.toString, "scala.collection.immutable.Map[String,String]") + } + @Test def constantFoldedOriginalTreeAttachment(): Unit = { val code = From d95d8a232c0825df80e4914b8dac37d6d9aafa7d Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 1 Sep 2022 14:52:07 +0200 Subject: [PATCH 009/261] [backport] with Xsource:2.13, include private[this] members in override checking --- .../tools/nsc/transform/OverridingPairs.scala | 14 ++++++++------ .../scala/tools/nsc/typechecker/Contexts.scala | 2 +- test/files/neg/t9334.check | 5 +++++ test/files/neg/t9334.scala | 8 ++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 test/files/neg/t9334.check create mode 100644 test/files/neg/t9334.scala diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index ceda2c30f600..5f8b2a026e44 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -25,15 +25,17 @@ abstract class OverridingPairs extends SymbolPairs { import global._ class Cursor(base: Symbol) extends super.Cursor(base) { + private lazy val isScala213 = settings.isScala213 + /** Symbols to exclude: Here these are constructors and private/artifact symbols, * including bridges. But it may be refined in subclasses. */ - override protected def exclude(sym: Symbol) = ( - sym.isPrivateLocal - || sym.isArtifact - || sym.isConstructor - || (sym.isPrivate && sym.owner != base) // Privates aren't inherited. Needed for pos/t7475a.scala - ) + override protected def exclude(sym: Symbol) = { + sym.isPrivateLocal && (sym.isParamAccessor || !isScala213) || + sym.isArtifact || + sym.isConstructor || + (sym.isPrivate && sym.owner != base) // Privates aren't inherited. Needed for pos/t7475a.scala + } /** Types always match. Term symbols match if their member types * relative to `self` match. diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 57c01dbf32cb..5d2931e3b710 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1248,7 +1248,7 @@ trait Contexts { self: Analyzer => var importLookupFor213MigrationWarning = false def depthOk213 = { - settings.isScala213 && !thisContext.unit.isJava && !cx(ContextMode.InPackageClauseName) && defSym.exists && isPackageOwnedInDifferentUnit(defSym) && { + currentRun.isScala213 && !thisContext.unit.isJava && !cx(ContextMode.InPackageClauseName) && defSym.exists && isPackageOwnedInDifferentUnit(defSym) && { importLookupFor213MigrationWarning = true true } diff --git a/test/files/neg/t9334.check b/test/files/neg/t9334.check new file mode 100644 index 000000000000..96eb564b14e7 --- /dev/null +++ b/test/files/neg/t9334.check @@ -0,0 +1,5 @@ +t9334.scala:7: error: overriding method aaa in class A of type => Int; + method aaa has weaker access privileges; it should not be private + private[this] def aaa: Int = 42 + ^ +one error found diff --git a/test/files/neg/t9334.scala b/test/files/neg/t9334.scala new file mode 100644 index 000000000000..53d264076acc --- /dev/null +++ b/test/files/neg/t9334.scala @@ -0,0 +1,8 @@ +// scalac: -Xsource:2.13 + +class A { + def aaa: Int = 10 +} +class B extends A { + private[this] def aaa: Int = 42 +} From 2a4f12ed1f00be4f97d7308a7b4c536ae8091779 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 31 Aug 2022 13:31:13 -0700 Subject: [PATCH 010/261] update .mailmap --- .mailmap | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.mailmap b/.mailmap index 6284a59103db..52a44cd06735 100644 --- a/.mailmap +++ b/.mailmap @@ -23,15 +23,18 @@ Christopher Vogt Damien Obristi Daniel C. Sobral Daniel C. Sobral +Daniel Esik Daniel Lorch Diego E. Alonso Blas Diego E. Alonso Blas +Eric Huang Erik Stenman Eugene Burmako Eugene Burmako Eugene Vigdorchik François Garillot Geoff Reedy +Gilad Hoch Harrison Houghton Ilya Sergei Ingo Maier @@ -79,3 +82,6 @@ Vincent Cremet Vladimir Nikolaev Vojin Jovanovic Vojin Jovanovic +Zhang Zhipeng +jxnu-liguobin +philwalk From d2725605f38ff34c745bfd15d20d79357ad47bb2 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 14 Sep 2022 18:05:20 -0700 Subject: [PATCH 011/261] New reference compiler is Scala 2.12.17 --- build.sbt | 2 +- project/MimaFilters.scala | 2 +- versions.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 5d12d053a7f3..4ffd348eee98 100644 --- a/build.sbt +++ b/build.sbt @@ -95,7 +95,7 @@ lazy val publishSettings : Seq[Setting[_]] = Seq( // should not be set directly. It is the same as the Maven version and derived automatically from `baseVersion` and // `baseVersionSuffix`. globalVersionSettings -(Global / baseVersion) := "2.12.17" +(Global / baseVersion) := "2.12.18" (Global / baseVersionSuffix) := "SNAPSHOT" (ThisBuild / organization) := "org.scala-lang" (ThisBuild / homepage) := Some(url("https://www.scala-lang.org")) diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index e3c67a5f0468..547c45bf82fd 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -13,7 +13,7 @@ object MimaFilters extends AutoPlugin { import autoImport._ override val globalSettings = Seq( - mimaReferenceVersion := Some("2.12.16"), + mimaReferenceVersion := Some("2.12.17"), ) val mimaFilters: Seq[ProblemFilter] = Seq[ProblemFilter]( diff --git a/versions.properties b/versions.properties index 8d04e82a3309..2bd815d40165 100644 --- a/versions.properties +++ b/versions.properties @@ -1,5 +1,5 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.12.16 +starr.version=2.12.17 # The scala.binary.version determines how modules are resolved. It is set as follows: # - After 2.x.0 is released, the binary version is 2.x From d0909167e5bf48089f92f4c109882131044d522f Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 14 Sep 2022 19:42:01 -0700 Subject: [PATCH 012/261] fix Scala Steward configuration at least, I think this will prevent us from getting any more unwanted PRs such as #10135 --- .scala-steward.conf | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.scala-steward.conf b/.scala-steward.conf index 3b05785b80cb..657196f675bf 100644 --- a/.scala-steward.conf +++ b/.scala-steward.conf @@ -2,17 +2,21 @@ # either. hopefully this is a reasonable compromise value? pullRequests.frequency = "14 days" -# only used internally, and they aren't ours (we aren't dogfooding -# them), and updates are unlikely to benefit us, so there's really no -# need to keep them current -updates.ignore = [ { groupId = "com.fasterxml.jackson.core"} ] -updates.ignore = [ { groupId = "org.slf4j"} ] -updates.ignore = [ { groupId = "org.eclipse.jgit"} ] -updates.ignore = [ { groupId = "org.openjdk.jol"} ] +updates.ignore = [ -# Ant support is deprecated, so leave the version where it is -updates.ignore = [ { groupId = "org.apache.ant"} ] + # only used internally, and they aren't ours (we aren't dogfooding + # them), and updates are unlikely to benefit us, so there's really no + # need to keep them current + { groupId = "com.fasterxml.jackson.core" }, + { groupId = "org.slf4j" }, + { groupId = "org.eclipse.jgit" }, + { groupId = "org.openjdk.jol" }, -# OSGi stuff is fragile and we suspect it is little-used, -# so let's prefer stability -updates.ignore = [ { groupId = "biz.aQute.bnd"} ] + # Ant support is deprecated, so leave the version where it is + { groupId = "org.apache.ant" }, + + # OSGi stuff is fragile and we suspect it is little-used, + # so let's prefer stability + { groupId = "biz.aQute.bnd" } + +] From 326ec0c61a639d6b43399249eae19398e1eb2697 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 14 Sep 2022 19:45:48 -0700 Subject: [PATCH 013/261] exclude more Jackson stuff from Steward --- .scala-steward.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/.scala-steward.conf b/.scala-steward.conf index 657196f675bf..17ba56cee95c 100644 --- a/.scala-steward.conf +++ b/.scala-steward.conf @@ -8,6 +8,7 @@ updates.ignore = [ # them), and updates are unlikely to benefit us, so there's really no # need to keep them current { groupId = "com.fasterxml.jackson.core" }, + { groupId = "com.fasterxml.jackson.dataformat" }, { groupId = "org.slf4j" }, { groupId = "org.eclipse.jgit" }, { groupId = "org.openjdk.jol" }, From 1be382c5ecb88d577569e477e7d12a6816a4398f Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 5 Sep 2022 17:37:12 +0000 Subject: [PATCH 014/261] Update jquery to 3.6.1 in 2.12.x --- project/ScaladocSettings.scala | 2 +- src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/ScaladocSettings.scala b/project/ScaladocSettings.scala index 4793b02e4f67..c1a8698ee8c0 100644 --- a/project/ScaladocSettings.scala +++ b/project/ScaladocSettings.scala @@ -7,7 +7,7 @@ object ScaladocSettings { // when this changes, the integrity check in HtmlFactory.scala also needs updating val webjarResources = Seq( - "org.webjars" % "jquery" % "3.6.0" + "org.webjars" % "jquery" % "3.6.1" ) def extractResourcesFromWebjar = Def.task { diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala index ccb711ee8dfd..d76c6c311a0a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -95,7 +95,7 @@ class HtmlFactory(val universe: doc.Universe, val reporter: Reporter) { ) final def webjarResources = List( - ("jquery.min.js", "/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=") + ("jquery.min.js", "o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=") ) /** Generates the Scaladoc site for a model into the site root. From 5b9c663d2e600e7cbf82594991534ac8c4eb9f19 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 15 Sep 2022 18:52:00 -0700 Subject: [PATCH 015/261] use modern sbt syntax in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 554ea20c7e13..944eca3a1379 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Once you've started an `sbt` session you can run one of the core commands: - Note that the `-bin` string marks the version binary compatible. Using it in sbt will cause the `scalaBinaryVersion` to be `2.12`. If the version is not binary compatible, we recommend using `-pre`, e.g., `2.13.0-pre-abcd123-SNAPSHOT`. - - Optionally `set publishArtifact in (Compile, packageDoc) in ThisBuild := false` + - Optionally `set ThisBuild / Compile / packageDoc / publishArtifact := false` to skip generating / publishing API docs (speeds up the process). If a command results in an error message like `a module is not authorized to depend on From 74955b8f235d94308ac5fcd9802d4ee93c64424b Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Fri, 16 Sep 2022 15:10:48 +0200 Subject: [PATCH 016/261] fix minor issues concerning large Vectors before implementing faster concatenation * misleading indentation * `a6 = new Arr6(LASTWIDTH)`: The whole point of LASTWIDTH is that Arr6 may be larger than the other arrays. This is considered in all initializations except here. The added test shows how initFrom failed so far on arrays with size in (Int.MaxValue/2, Int.MaxValue] and works now. * `else if (xor < WIDTH6) { // level = 5`: Similarly to the last one, Arr6 may have a size up to LASTWIDTH, so this check fails for valid Arr6s with size in [32,64]. About WIDTH6, see next point. * `BITS6` and `WIDTH6`: There is no valid usage for them. The BITSn and WIDHTn variables mark which bits of the index correspond to which level, or more precisely Vectors up to level n have sizes of [0, WIDTHn). Thus, the correct value of BITS6 would be (BITS * 6 + 1), thus WIDTH6 would be Int.MaxValue+1, one "more" than the longest possible vector of level 6. The current value is just wrong, as it arbitrarily "cuts" the range of 6 bits which compose the index in Arr6 in 1 high and 5 lower bits. In fact, the line above was the only place where one of `BITS6` and `WIDTH6` was used. I suspect this was a little oversight because of the irregularity of level 6. --- .../scala/collection/immutable/Vector.scala | 16 +++++++--------- .../scala/collection/immutable/VectorTest.scala | 13 ++++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 9edbf2bc9b90..10b56dd96e26 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -289,11 +289,11 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va override final def foreach[U](f: A => U): Unit = { val c = vectorSliceCount - var i = 0 - while(i < c) { - foreachRec(vectorSliceDim(c, i)-1, vectorSlice(i), f) - i += 1 - } + var i = 0 + while (i < c) { + foreachRec(vectorSliceDim(c, i) - 1, vectorSlice(i), f) + i += 1 + } } // The following definitions are needed for binary compatibility with ParVector @@ -1538,7 +1538,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { depth = 6 offset = WIDTH5 - v6.len12345 setLen(v6.length0 + offset) - a6 = new Arr6(WIDTH) + a6 = new Arr6(LASTWIDTH) a6(0) = copyPrepend(copyPrepend(copyPrepend(copyPrepend(v6.prefix1, v6.prefix2), v6.prefix3), v6.prefix4), v6.prefix5) System.arraycopy(d6, 0, a6, 1, d6.length) a5 = copyOf(s5, WIDTH) @@ -1641,7 +1641,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 a5((idx >>> BITS4) & MASK) = a4 - } else if (xor < WIDTH6) { // level = 5 + } else if (xor > 0) { // level = 5 if (depth == 5) { a6 = new Array(LASTWIDTH); a6(0) = a5; depth += 1 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) @@ -1771,8 +1771,6 @@ private[immutable] object VectorInline { final val WIDTH4 = 1 << BITS4 final val BITS5 = BITS * 5 final val WIDTH5 = 1 << BITS5 - final val BITS6 = BITS * 6 - final val WIDTH6 = 1 << BITS6 final val LASTWIDTH = WIDTH << 1 // 1 extra bit in the last level to go up to Int.MaxValue (2^31-1) instead of 2^30: final val Log2ConcatFaster = 5 diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index d5487d2ffe5b..7acf04abecb4 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -47,7 +47,7 @@ class VectorTest { @Test def hasCorrectAppendedAndPrependedAll(): Unit = { - val els = Vector(1 to 1000: _*) + val els = Vector(1 to 1200: _*) for (i <- 0 until els.size) { val (prefix, suffix) = els.splitAt(i) @@ -61,6 +61,17 @@ class VectorTest { } } + @Test + def testBuilderInitWithLargeVector(): Unit = { + val v = Vector.fillSparse(Int.MaxValue / 4 * 3)("v") + val copy = + new VectorBuilder[String] + .initFrom(v) + .result() + assertEquals(copy.size, v.size) + assertEquals(copy.take(500), v.take(500)) + } + @Test def factoryReuse(): Unit = { assertSame(Vector.empty, Vector.empty) From d7d696ba2acde38e025aaedd92d708bd07dae7ef Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Fri, 16 Sep 2022 19:48:24 +0200 Subject: [PATCH 017/261] reuse Arrays in VectorBuilder.addVector(..) where possible --- .../scala/collection/immutable/Vector.scala | 66 ++++++++++++++++--- .../immutable/VectorConcatBenchmark.scala | 52 +++++++++++++++ .../collection/immutable/VectorTest.scala | 27 ++++++++ 3 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 10b56dd96e26..fe3221bd08fd 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -1582,6 +1582,40 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { } } + private[this] def arrN(dim: Int): Array[AnyRef] = dim match { + case 1 => a1 + case 2 => a2.asInstanceOf[Array[AnyRef]] + case 3 => a3.asInstanceOf[Array[AnyRef]] + case 4 => a4.asInstanceOf[Array[AnyRef]] + case 5 => a5.asInstanceOf[Array[AnyRef]] + case 6 => a6.asInstanceOf[Array[AnyRef]] + } + + private[this] def addArrN(slice: Array[AnyRef], dim: Int): Unit = { + assert(dim >= 2) + if (slice.isEmpty) return + if (len1 == WIDTH) advance() + val sl = slice.length + val bits = if (dim == 6) BITS * dim + 1 else BITS * dim + val lBits = BITS * (dim-1) + val width = 1 << bits + val lWidth = 1 << lBits + val lowerMask = lWidth - 1 + val mask = if (dim == 6) -1 else MASK + if ((lenRest & lowerMask) == 0) { // lenRest is multiple of lWidth, so the elements of this slice do not need to be split, so they can be reused + val copy1 = mmin(((width - lenRest) >>> lBits) & mask, sl) + val copy2 = sl - copy1 + System.arraycopy(slice, 0, arrN(dim), (lenRest >>> lBits) & MASK, copy1) + advanceN(lWidth * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, arrN(dim), 0, copy2) + advanceN(lWidth * copy2) + } + } else { // this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], dim-1)) + } + } + private[this] def addVector(xs: Vector[A]): this.type = { val sliceCount = xs.vectorSliceCount var sliceIdx = 0 @@ -1589,6 +1623,8 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { val slice = xs.vectorSlice(sliceIdx) vectorSliceDim(sliceCount, sliceIdx) match { case 1 => addArr1(slice.asInstanceOf[Arr1]) + case n if len1 == WIDTH || len1 == 0 => + addArrN(slice.asInstanceOf[Array[AnyRef]], n) case n => foreachRec(n-2, slice, addArr1) } sliceIdx += 1 @@ -1612,19 +1648,30 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { advance1(idx, xor) } + private[this] def advanceN(n: Int): Unit = if (n > 0) { + // assert(n % 32 == 0) + val idx = lenRest + n + val xor = idx ^ lenRest + lenRest = idx + len1 = 0 + advance1(idx, xor) + } + private[this] def advance1(idx: Int, xor: Int): Unit = { - if (xor < WIDTH2) { // level = 1 - if (depth == 1) { a2 = new Array(WIDTH); a2(0) = a1; depth += 1 } + if (xor <= 0) { // level = 6 or something very unexpected happened + throw new IllegalArgumentException(s"advance1($idx, $xor): a1=$a1, a2=$a2, a3=$a3, a4=$a4, a5=$a5, a6=$a6, depth=$depth") + } else if (xor < WIDTH2) { // level = 1 + if (depth <= 1) { a2 = new Array(WIDTH); a2(0) = a1; depth = 2 } a1 = new Array(WIDTH) a2((idx >>> BITS) & MASK) = a1 } else if (xor < WIDTH3) { // level = 2 - if (depth == 2) { a3 = new Array(WIDTH); a3(0) = a2; depth += 1 } + if (depth <= 2) { a3 = new Array(WIDTH); a3(0) = a2; depth = 3 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a2((idx >>> BITS) & MASK) = a1 a3((idx >>> BITS2) & MASK) = a2 } else if (xor < WIDTH4) { // level = 3 - if (depth == 3) { a4 = new Array(WIDTH); a4(0) = a3; depth += 1 } + if (depth <= 3) { a4 = new Array(WIDTH); a4(0) = a3; depth = 4 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a3 = new Array(WIDTH) @@ -1632,7 +1679,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 } else if (xor < WIDTH5) { // level = 4 - if (depth == 4) { a5 = new Array(WIDTH); a5(0) = a4; depth += 1 } + if (depth <= 4) { a5 = new Array(WIDTH); a5(0) = a4; depth = 5 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a3 = new Array(WIDTH) @@ -1641,8 +1688,8 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 a5((idx >>> BITS4) & MASK) = a4 - } else if (xor > 0) { // level = 5 - if (depth == 5) { a6 = new Array(LASTWIDTH); a6(0) = a5; depth += 1 } + } else { // level = 5 + if (depth <= 5) { a6 = new Array(LASTWIDTH); a6(0) = a5; depth = 6 } a1 = new Array(WIDTH) a2 = new Array(WIDTH) a3 = new Array(WIDTH) @@ -1652,9 +1699,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { a3((idx >>> BITS2) & MASK) = a2 a4((idx >>> BITS3) & MASK) = a3 a5((idx >>> BITS4) & MASK) = a4 - a6((idx >>> BITS5) & MASK) = a5 - } else { // level = 6 - throw new IllegalArgumentException(s"advance1($idx, $xor): a1=$a1, a2=$a2, a3=$a3, a4=$a4, a5=$a5, a6=$a6, depth=$depth") + a6(idx >>> BITS5) = a5 } } @@ -1662,6 +1707,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { val len = len1 + lenRest val realLen = len - offset if(realLen == 0) Vector.empty + else if(len < 0) throw new IndexOutOfBoundsException(s"Vector cannot have negative size $len") else if(len <= WIDTH) { if(realLen == WIDTH) new Vector1(a1) else new Vector1(copyOf(a1, realLen)) diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala new file mode 100644 index 000000000000..6884de6fe03a --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala @@ -0,0 +1,52 @@ +package scala.collection.immutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(1) +@Threads(1) +@Warmup(iterations = 4) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorConcatBenchmark { + @Param(Array("30", "32", "1000", "1024", "30000", "32000", "30720", "32768", "1048576", "33554432")) + var size: Int = _ + + val o = new AnyRef + + var vAligned: Vector[AnyRef] = _ + var vShifted: Vector[AnyRef] = _ + + @Setup(Level.Trial) def init: Unit = { + vAligned = Vector.fillSparse(size)(o) + vShifted = Vector.fillSparse(size + 5)(o).drop(5) + } + + def concat(bh: Blackhole, a: Vector[AnyRef], b: Vector[AnyRef], times: Int = 10): Any = { + var coll = a + val coll1 = b + var i = 0 + while(i < 10) { + coll = coll.appendedAll(coll1) + i += 1 + } + bh.consume(coll) + } + + @Benchmark def concatAlignedAligned(bh: Blackhole): Any = + concat(bh, vAligned, vAligned) + + @Benchmark def concatAlignedShifted(bh: Blackhole): Any = + concat(bh, vShifted, vShifted) + + + @Benchmark def concatMisalignedAligned(bh: Blackhole): Any = + concat(bh, vAligned, vShifted) + + @Benchmark def concatMisalignedShifted(bh: Blackhole): Any = + concat(bh, vShifted, vAligned) +} diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index 7acf04abecb4..6511caf9d465 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -61,6 +61,33 @@ class VectorTest { } } + @Test + def testBuilderAddVector(): Unit = { + import VectorInline._ + val b = new VectorBuilder[Int] + val expected1 = Vector.from(1 to 32).asInstanceOf[Vector1[Int]] + b.initFrom(expected1) + val expected2 = Vector.from(33 to 128).asInstanceOf[Vector2[Int]] + b.addAll(expected2) // uses addVector with Vector2, aligned + b.addOne(129) + val expected3 = Vector.from(130 to 224).asInstanceOf[Vector2[Int]] + b.addAll(expected3) // uses addVector with Vector2, but misaligned + b.addAll(Vector.from(225 to 4096)) // uses addVector with Vector3, aligned to 32, but not 1024 + b.addAll(Vector.from(4097 to 8192)) // uses addVector with Vector3, aligned to 1024 + b.addOne(8193) // builder still working for single element? + b.addAll(Vector.from(8193 to 234567).tail) // aligned to 1024, split at arbitrary number 234567 + b.addAll(Vector.from(1 to 42 * WIDTH3).drop(234567)) // aligned to pow(32,3) + val res = b.result().asInstanceOf[Vector5[Int]] + assertEquals(1 to 42 * WIDTH3, res) + + assertSame("b.initFrom did not keep original array but copied instead", expected1.prefix1, res.prefix1) + +// assertSame(s"b.addVector did not keep original array but copied instead (${expected2.prefix1.head},...,${expected2.prefix1.last} vs ${res.prefix2(0).head},...,${res.prefix2(0).last}).", expected2.prefix1, res.prefix2(0)) // prefix1 is not reused, as addArr1 is called + assertSame("b.addVector did not keep original array but copied instead", expected2.data2(0), res.prefix2(1)) // expected2's arrays are reused + + assertTrue("", expected3.suffix1.length != 32) // expected3 is misaligned + } + @Test def testBuilderInitWithLargeVector(): Unit = { val v = Vector.fillSparse(Int.MaxValue / 4 * 3)("v") From 16c8a4efaba40553b80e3e0678c98867580bac4a Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 15 Sep 2022 18:52:13 -0700 Subject: [PATCH 018/261] get rid of some warnings noise --- src/compiler/scala/tools/nsc/Global.scala | 1 - src/compiler/scala/tools/reflect/package.scala | 2 +- test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index a3eb593acd23..b52b63994e2c 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -33,7 +33,6 @@ import scala.tools.nsc.io.{AbstractFile, SourceReader} import scala.tools.nsc.plugins.Plugins import scala.tools.nsc.profile.Profiler import scala.tools.nsc.reporters.{FilteringReporter, MakeFilteringForwardingReporter, Reporter} -import scala.tools.nsc.settings.StandardScalaSettings import scala.tools.nsc.symtab.classfile.Pickler import scala.tools.nsc.symtab.{Flags, SymbolTable, SymbolTrackers} import scala.tools.nsc.transform._ diff --git a/src/compiler/scala/tools/reflect/package.scala b/src/compiler/scala/tools/reflect/package.scala index 2c058ed47bc8..012e6309988e 100644 --- a/src/compiler/scala/tools/reflect/package.scala +++ b/src/compiler/scala/tools/reflect/package.scala @@ -87,7 +87,7 @@ package object reflect { val NSC_ERROR = ERROR def doReport(pos: Position, msg: String, nscSeverity: NscSeverity): Unit = - frontEnd.log(pos, msg, nscSeverity match { + frontEnd.log(pos, msg, (nscSeverity: @unchecked) match { case NSC_INFO => API_INFO case NSC_WARNING => API_WARNING case NSC_ERROR => API_ERROR diff --git a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala index 0bb23c6b5f23..0b110809a801 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala @@ -295,8 +295,8 @@ class BytecodeTest extends BytecodeTesting { ("x$1", true), // captured, assigned in constructor ("y$1", true) // captured ), fs) - val assignedInConstr = getMethod(k, "").instructions.filter(_.opcode == Opcodes.PUTFIELD) map { - case f: Field => f.name + val assignedInConstr = getMethod(k, "").instructions.collect { + case f: Field if f.opcode == Opcodes.PUTFIELD => f.name } assertEquals(List("$outer", "x$1", "y$1"), assignedInConstr.sorted) } From 8e6ae7cf7e0168e05622c6c06870332d0ea934d2 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Fri, 16 Sep 2022 16:24:54 -0700 Subject: [PATCH 019/261] use MiMa 1.1.1 (was 1.1.0) dogfood: delicious! --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index e06a9c0db5bd..c1c09a6780a6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -18,7 +18,7 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.0") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.1") libraryDependencies ++= Seq( "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.9.201909030838-r", From 6c530acf9e7308c1740ce9b601da129958b48fe0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 21 Sep 2022 11:37:43 -0700 Subject: [PATCH 020/261] Lint if args adapted to Unit and discarded --- .../scala/tools/nsc/settings/Warnings.scala | 2 ++ .../tools/nsc/typechecker/Adaptations.scala | 16 ++++++++++++---- test/files/neg/t12495.check | 12 ++++++++++++ test/files/neg/t12495.scala | 5 +++++ 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 test/files/neg/t12495.check create mode 100644 test/files/neg/t12495.scala diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index b5d12f83683a..1a287f8efe53 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -213,6 +213,7 @@ trait Warnings { val MultiargInfix = LintWarning("multiarg-infix", "Infix operator was defined or used with multiarg operand.") val ImplicitRecursion = LintWarning("implicit-recursion", "Implicit resolves to an enclosing definition.") val UniversalMethods = LintWarning("universal-methods", "Require arg to is/asInstanceOf.") + val ArgDiscard = LintWarning("arg-discard", "-Wvalue-discard for adapted arguments.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -246,6 +247,7 @@ trait Warnings { def multiargInfix = lint contains MultiargInfix def lintImplicitRecursion = lint.contains(ImplicitRecursion) || (warnSelfImplicit.value: @nowarn("cat=deprecation")) def lintUniversalMethods = lint.contains(UniversalMethods) + def lintArgDiscard = lint.contains(ArgDiscard) // The Xlint warning group. val lint = MultiChoiceSetting( diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 4fc3c1fdddd8..cee889f74e4b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -77,11 +77,11 @@ trait Adaptations { ) } @inline def msg(what: String): String = s"adaptation of an empty argument list by inserting () $what" - @inline def noAdaptation = { + @inline def noAdaptation: false = { context.error(t.pos, adaptWarningMessage(msg("has been removed"), showAdaptation = false)) false // drop adaptation } - @inline def deprecatedAdaptation = { + @inline def deprecatedAdaptation: true = { val twist = if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous" else "this is unlikely to be what you want" @@ -89,8 +89,16 @@ trait Adaptations { context.deprecationWarning(t.pos, t.symbol, adaptWarningMessage(text), "2.11.0") true // keep adaptation } - @inline def warnAdaptation = { - if (settings.warnAdaptedArgs && !isInfix) context.warning(t.pos, adaptWarningMessage( + @inline def warnAdaptation: true = { + def discardedArgs = t match { + case Apply(_, stat @ Block(Apply(TypeApply(Select(adapter, _), _), adapted) :: Nil, expr) :: Nil) => + isTupleSymbol(adapter.symbol.companion) && expr.tpe == UnitTpe && adapted == args + case _ => false + } + if (settings.lintArgDiscard && discardedArgs) context.warning(t.pos, adaptWarningMessage( + s"adapted the argument list to expected Unit type: arguments will be discarded"), + WarningCategory.LintAdaptedArgs) + else if (settings.warnAdaptedArgs && !isInfix) context.warning(t.pos, adaptWarningMessage( s"adapted the argument list to the expected ${args.size}-tuple: add additional parens instead"), WarningCategory.LintAdaptedArgs) true // keep adaptation diff --git a/test/files/neg/t12495.check b/test/files/neg/t12495.check new file mode 100644 index 000000000000..52c058692538 --- /dev/null +++ b/test/files/neg/t12495.check @@ -0,0 +1,12 @@ +t12495.scala:4: warning: discarded non-Unit value of type (Int, Int) + def g = f(42, 27) + ^ +t12495.scala:4: warning: adapted the argument list to expected Unit type: arguments will be discarded + signature: Function1.apply(v1: T1): R + given arguments: 42, 27 + after adaptation: Function1((42, 27): Unit) + def g = f(42, 27) + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12495.scala b/test/files/neg/t12495.scala new file mode 100644 index 000000000000..5be5e3796c22 --- /dev/null +++ b/test/files/neg/t12495.scala @@ -0,0 +1,5 @@ +// scalac: -Werror -Xlint:arg-discard,adapted-args -Wvalue-discard +class C { + val f = (u: Unit) => println(s"[$u]") + def g = f(42, 27) +} From 6abbe43976a3fc4075cd695630a486669e6510d7 Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Fri, 16 Sep 2022 20:14:00 +0200 Subject: [PATCH 021/261] add VectorBuilder.alignTo and fast Vector.prependedAll * add alignTo and leftAlignPrefix in VectorBuilder: alignTo(n: Int, v: Vector) ensures that if there are added n elements before Vector v, the Builder is aligned to the structure of v. This way, the Arrays of v can be reused in the Builder, which improves performance drastically. Formally, alignTo sets `len1`, `lenRest` and the internal arrays as if there were $x = len1 + lenRest$ elements already added, such that $x + n + prefixLenght(v)$ is a multiple of the maximum prefix length of a Vector of v's level. The number $x$ is stored in `offset` and the new variable `prefixIsRightAligned` is set true to record the fact that in each prefix array, the length is WIDTH and the content is right-aligned. This is different to the normal case, where the prefixes are either full and size WIDTH or, if `initFrom` was used, full and trimmed. To remove the leading null entries in the prefix vectors, a call to leftAlignPrefixes is needed. This is done at the beginning of result(). * prependedAll0 and appendedAll0: In case that an IterableOnce (left side; may be a Vector itself) and a Vector (right side) are concatenated and the left one is shorter than the right Vector, a VectorBuilder aligned to the right Vector is used. `++` and `concat` forward to `appendedAll`, so they profit from this optimization. * add tests and benchmark --- .../scala/collection/immutable/Vector.scala | 112 +++++++++++++++++- .../immutable/VectorConcatBenchmark.scala | 4 +- .../VectorConcatBigVectorsBenchmark.scala | 45 +++++++ .../collection/immutable/VectorTest.scala | 40 +++++++ 4 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index fe3221bd08fd..30f3d383971d 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -217,6 +217,8 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va val it = this.iterator while (it.hasNext) v = v :+ it.next() v + } else if (prefix.knownSize >= 0 && prefix.knownSize < this.size) { + new VectorBuilder[B].alignTo(prefix.knownSize, this).addAll(prefix).addAll(this).result() } else super.prependedAll(prefix) } @@ -234,6 +236,9 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va val ri = this.reverseIterator while (ri.hasNext) v = v.prepended(ri.next()) v + } else if (this.size < suffix.knownSize && suffix.isInstanceOf[Vector[_]]) { + val v = suffix.asInstanceOf[Vector[B]] + new VectorBuilder[B].alignTo(this.size, v).addAll(this).addAll(v).result() } else new VectorBuilder[B].initFrom(this).addAll(suffix).result() } @@ -1394,6 +1399,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { private[this] var a2: Arr2 = _ private[this] var a1: Arr1 = new Arr1(WIDTH) private[this] var len1, lenRest, offset = 0 + private[this] var prefixIsRightAligned = false private[this] var depth = 1 @inline private[this] final def setLen(i: Int): Unit = { @@ -1417,6 +1423,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { len1 = 0 lenRest = 0 offset = 0 + prefixIsRightAligned = false depth = 1 } @@ -1559,6 +1566,108 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { this } + //TODO Make public; this method is only private for binary compatibility + private[collection] def alignTo(before: Int, bigVector: Vector[A]): this.type = { + if (len1 != 0 || lenRest != 0) + throw new UnsupportedOperationException("A non-empty VectorBuilder cannot be aligned retrospectively. Please call .reset() or use a new VectorBuilder.") + val (prefixLength, maxPrefixLength) = bigVector match { + case Vector0 => (0, 1) + case v1: Vector1[_] => (0, 1) + case v2: Vector2[_] => (v2.len1, WIDTH) + case v3: Vector3[_] => (v3.len12, WIDTH2) + case v4: Vector4[_] => (v4.len123, WIDTH3) + case v5: Vector5[_] => (v5.len1234, WIDTH4) + case v6: Vector6[_] => (v6.len12345, WIDTH5) + } + if (maxPrefixLength == 1) return this // does not really make sense to align for <= 32 element-vector + val overallPrefixLength = (before + prefixLength) % maxPrefixLength + offset = (maxPrefixLength - overallPrefixLength) % maxPrefixLength + // pretend there are already `offset` elements added + advanceN(offset & ~MASK) + len1 = offset & MASK + prefixIsRightAligned = true + this + } + + /** + * Removes `offset` leading `null`s in the prefix. + * This is needed after calling `alignTo` and subsequent additions, + * directly before the result is used for creating a new Vector. + * + * example: + * a2 = Array(null, ..., null, Array(null, .., null, 0, 1, .., x), Array(x+1, .., x+32), ...) + * becomes + * a2 = Array(Array(0, 1, .., x), Array(x+1, .., x+32), ...) + */ + private[this] def leftAlignPrefix(): Unit = { + var a: Array[AnyRef] = null // the array we modify + var aParent: Array[AnyRef] = null // a's parent, so aParent(0) == a + if (depth >= 6) { + a = a6.asInstanceOf[Array[AnyRef]] + val i = offset >>> BITS5 + if (i > 0) { + a = copyOfRange(a, i, LASTWIDTH) + a6 = a.asInstanceOf[Arr6] + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 5) { + if (a == null) a = a5.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS4) & MASK + if (i > 0) { + a = copyOfRange(a, i, WIDTH) + if (depth == 5) a5 = a.asInstanceOf[Arr5] + else aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 4) { + if (a == null) a = a4.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS3) & MASK + if (i > 0) { + a = copyOfRange(a, i, WIDTH) + if (depth == 4) a4 = a.asInstanceOf[Arr4] + else aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 3) { + if (a == null) a = a3.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS2) & MASK + if (i > 0) { + a = copyOfRange(a, i, WIDTH) + if (depth == 3) a3 = a.asInstanceOf[Arr3] + else aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 2) { + if (a == null) a = a2.asInstanceOf[Array[AnyRef]] + val i = (offset >>> BITS) & MASK + if (i > 0) { + a = copyOfRange(a, i, WIDTH) + if (depth == 2) a2 = a.asInstanceOf[Arr2] + else aParent(0) = a + } + aParent = a + a = a(0).asInstanceOf[Array[AnyRef]] + } + if (depth >= 1) { + if (a == null) a = a1.asInstanceOf[Array[AnyRef]] + val i = offset & MASK + if (i > 0) { + a = copyOfRange(a, i, WIDTH) + if (depth == 1) a1 = a.asInstanceOf[Arr1] + else aParent(0) = a + } + } + prefixIsRightAligned = false + } + def addOne(elem: A): this.type = { if(len1 == WIDTH) advance() a1(len1) = elem.asInstanceOf[AnyRef] @@ -1634,7 +1743,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { override def addAll(xs: IterableOnce[A]): this.type = xs match { case v: Vector[_] => - if(len1 == 0 && lenRest == 0) initFrom(v) + if(len1 == 0 && lenRest == 0 && !prefixIsRightAligned) initFrom(v) else addVector(v.asInstanceOf[Vector[A]]) case _ => super.addAll(xs) @@ -1704,6 +1813,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { } def result(): Vector[A] = { + if (prefixIsRightAligned) leftAlignPrefix() val len = len1 + lenRest val realLen = len - offset if(realLen == 0) Vector.empty diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala index 6884de6fe03a..ad950cb0f865 100644 --- a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBenchmark.scala @@ -21,7 +21,7 @@ class VectorConcatBenchmark { var vAligned: Vector[AnyRef] = _ var vShifted: Vector[AnyRef] = _ - @Setup(Level.Trial) def init: Unit = { + @Setup(Level.Trial) def init(): Unit = { vAligned = Vector.fillSparse(size)(o) vShifted = Vector.fillSparse(size + 5)(o).drop(5) } @@ -30,7 +30,7 @@ class VectorConcatBenchmark { var coll = a val coll1 = b var i = 0 - while(i < 10) { + while(i < times) { coll = coll.appendedAll(coll1) i += 1 } diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala new file mode 100644 index 000000000000..72b3122acc48 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatBigVectorsBenchmark.scala @@ -0,0 +1,45 @@ +package scala.collection.immutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(1) +@Threads(1) +@Warmup(iterations = 4) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorConcatBigVectorsBenchmark { + val size: Int = 1000000 + + @Param(Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10")) + var tenthsSplit: Int = _ + val o = new AnyRef + + var l: Vector[AnyRef] = _ + var lShifted: Vector[AnyRef] = _ + var r: Vector[AnyRef] = _ + var rNew: Vector[AnyRef] = _ + + @Setup(Level.Trial) def init(): Unit = { + val split = size * tenthsSplit / 10 + + val (a, b) = Vector.fillSparse(size)(o).splitAt(split) + l = a; r = b + rNew = Vector.fillSparse(size - split)(o) + lShifted = Vector.fillSparse(split + 5)(o).drop(5) + } + + @Benchmark def concatAligned(bh: Blackhole): Any = + bh.consume(l ++ r) + + @Benchmark def concatSemiAligned(bh: Blackhole): Any = + bh.consume(l ++ rNew) + + @Benchmark def concatMisaligned(bh: Blackhole): Any = + bh.consume(lShifted ++ r) + +} diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index 6511caf9d465..a9b77d4b9745 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -88,6 +88,46 @@ class VectorTest { assertTrue("", expected3.suffix1.length != 32) // expected3 is misaligned } + @Test + def testBuilderAlignTo1(): Unit = { + // v3 == Vector(0, 1, ..., 1999), but alignment is no multiple of 32 or 1024 + val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] + for (i <- Seq(0, 5, 123, 949, 950, 982, 1024, 1999, 2000)) { + val (a, b) = v3.splitAt(i) + val res = new VectorBuilder[AnyRef] + .alignTo(i, b) + .addAll(a.toList) // ensure there is no alignment in a + .addAll(b) + .result() + .asInstanceOf[Vector3[AnyRef]] + assertEquals(s"values equal when split at $i", v3, res) + if (i < 950) // (v3.prefix1++v3.prefix2).size == 982. So when dropping >= 950 elements, and keeping prefix1 nonempty (=> has 32 elements), prefix2 would be empty. Instead, suffix2's content is stored in prefix2, so the alignment (len12) changes and it's okay. + assertEquals(s"alignment is the same when split at $i", v3.len12, res.len12) + } + } + + @Test + def testBuilderAlignTo2(): Unit = { + val Large = 1 << 20 + for ( + size <- Seq(0, 1, 31, 1 << 5, 1 << 10, 1 << 15, 1 << 20, 1 << 25, 1 << 30, (1 << 31) - (1 << 26) - 1000); + i <- Seq(0, 1, 5, 123) + ) { +// println((i, size)) + val v = if (size < Large) Vector.tabulate(size)(_.toString) else Vector.fillSparse(size)("v") + val prefix = Vector.fill(i)("prefix") + val res = new VectorBuilder[AnyRef] + .alignTo(i, v) + .addAll(prefix) + .addAll(v) + .result() + val vDesc = if (v.headOption.contains("v")) s"Vector(\"v\")*$size" else s"Vector(0,..,${size-1})" + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).size", size + i, res.size) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).take($i)", prefix, res.take(i)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).drop($i)", v.take(Large), res.drop(i).take(Large)) + } + } + @Test def testBuilderInitWithLargeVector(): Unit = { val v = Vector.fillSparse(Int.MaxValue / 4 * 3)("v") From e4b872afd9d197daf9e73dca0fbd289e2cf49d71 Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Fri, 16 Sep 2022 11:57:51 +0200 Subject: [PATCH 022/261] expand bit-manipulations to switch in Vector.addArrN --- .../scala/collection/immutable/Vector.scala | 104 +++++++++++++----- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 30f3d383971d..7dd3334e7d9e 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -1691,37 +1691,85 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { } } - private[this] def arrN(dim: Int): Array[AnyRef] = dim match { - case 1 => a1 - case 2 => a2.asInstanceOf[Array[AnyRef]] - case 3 => a3.asInstanceOf[Array[AnyRef]] - case 4 => a4.asInstanceOf[Array[AnyRef]] - case 5 => a5.asInstanceOf[Array[AnyRef]] - case 6 => a6.asInstanceOf[Array[AnyRef]] - } - private[this] def addArrN(slice: Array[AnyRef], dim: Int): Unit = { - assert(dim >= 2) +// assert(dim >= 2) +// assert(lenRest % WIDTH == 0) +// assert(len1 == 0 || len1 == WIDTH) if (slice.isEmpty) return if (len1 == WIDTH) advance() - val sl = slice.length - val bits = if (dim == 6) BITS * dim + 1 else BITS * dim - val lBits = BITS * (dim-1) - val width = 1 << bits - val lWidth = 1 << lBits - val lowerMask = lWidth - 1 - val mask = if (dim == 6) -1 else MASK - if ((lenRest & lowerMask) == 0) { // lenRest is multiple of lWidth, so the elements of this slice do not need to be split, so they can be reused - val copy1 = mmin(((width - lenRest) >>> lBits) & mask, sl) - val copy2 = sl - copy1 - System.arraycopy(slice, 0, arrN(dim), (lenRest >>> lBits) & MASK, copy1) - advanceN(lWidth * copy1) - if (copy2 > 0) { - System.arraycopy(slice, copy1, arrN(dim), 0, copy2) - advanceN(lWidth * copy2) - } - } else { // this slice does not align, need to try lower dimensions - slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], dim-1)) + val sl = slice.length + (dim: @switch) match { + case 2 => + // lenRest is always a multiple of WIDTH + val copy1 = mmin(((WIDTH2 - lenRest) >>> BITS) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS) & MASK + System.arraycopy(slice, 0, a2, destPos, copy1) + advanceN(WIDTH * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a2, 0, copy2) + advanceN(WIDTH * copy2) + } + case 3 => + if (lenRest % WIDTH2 != 0) { + // lenRest is not multiple of WIDTH2, so this slice does not align, need to try lower dimension + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 2)) + return + } + val copy1 = mmin(((WIDTH3 - lenRest) >>> BITS2) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS2) & MASK + System.arraycopy(slice, 0, a3, destPos, copy1) + advanceN(WIDTH2 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a3, 0, copy2) + advanceN(WIDTH2 * copy2) + } + case 4 => + if (lenRest % WIDTH3 != 0) { + // lenRest is not multiple of WIDTH3, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 3)) + return + } + val copy1 = mmin(((WIDTH4 - lenRest) >>> BITS3) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS3) & MASK + System.arraycopy(slice, 0, a4, destPos, copy1) + advanceN(WIDTH3 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a4, 0, copy2) + advanceN(WIDTH3 * copy2) + } + case 5 => + if (lenRest % WIDTH4 != 0) { + // lenRest is not multiple of WIDTH4, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 4)) + return + } + val copy1 = mmin(((WIDTH5 - lenRest) >>> BITS4) & MASK, sl) + val copy2 = sl - copy1 + val destPos = (lenRest >>> BITS4) & MASK + System.arraycopy(slice, 0, a5, destPos, copy1) + advanceN(WIDTH4 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a5, 0, copy2) + advanceN(WIDTH4 * copy2) + } + case 6 => // note width is now LASTWIDTH + if (lenRest % WIDTH5 != 0) { + // lenRest is not multiple of WIDTH5, so this slice does not align, need to try lower dimensions + slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 5)) + return + } + val copy1 = mmin((BITS * 6 + 1 - lenRest) >>> BITS5, sl) + val copy2 = sl - copy1 + val destPos = lenRest >>> BITS5 + System.arraycopy(slice, 0, a6, destPos, copy1) + advanceN(WIDTH5 * copy1) + if (copy2 > 0) { + System.arraycopy(slice, copy1, a6, 0, copy2) + advanceN(WIDTH5 * copy2) + } } } From 26686f35e4c468de68743c28eea19ad31d29c3cb Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 25 Sep 2022 12:25:45 -0700 Subject: [PATCH 023/261] Clarify spec of root contexts --- spec/02-identifiers-names-and-scopes.md | 67 ++++++++----------- .../tools/nsc/typechecker/Contexts.scala | 29 +++----- test/files/neg/t12513.check | 4 ++ test/files/neg/t12513/predefer_2.scala | 12 ++++ test/files/neg/t12513/predefined_1.scala | 7 ++ test/files/neg/t12513b.check | 4 ++ test/files/neg/t12513b.scala | 9 +++ test/files/pos/t12513c/c.scala | 1 + test/files/pos/t12513c/xy.scala | 3 + 9 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 test/files/neg/t12513.check create mode 100644 test/files/neg/t12513/predefer_2.scala create mode 100644 test/files/neg/t12513/predefined_1.scala create mode 100644 test/files/neg/t12513b.check create mode 100644 test/files/neg/t12513b.scala create mode 100644 test/files/pos/t12513c/c.scala create mode 100644 test/files/pos/t12513c/xy.scala diff --git a/spec/02-identifiers-names-and-scopes.md b/spec/02-identifiers-names-and-scopes.md index b8bde8cfd1a9..855e6844af12 100644 --- a/spec/02-identifiers-names-and-scopes.md +++ b/spec/02-identifiers-names-and-scopes.md @@ -14,16 +14,17 @@ collectively called _entities_. Names are introduced by local [package clauses](09-top-level-definitions.html#packagings) which are collectively called _bindings_. -Bindings of different kinds have precedence defined on them: +Bindings of each kind are assigned a precedence which determines +whether one binding can shadow another: 1. Definitions and declarations that are local, inherited, or made available by a package clause and also defined in the same compilation unit as the reference to them, have the highest precedence. 1. Explicit imports have the next highest precedence. 1. Wildcard imports have the next highest precedence. -1. Definitions made available by a package clause, but not also defined in the - same compilation unit as the reference to them, as well as imports which - are supplied by the compiler but not explicitly written in source code, +1. Bindings made available by a package clause, but not also defined in the + same compilation unit as the reference to them, as well as bindings + supplied by the compiler but not explicitly written in source code, have the lowest precedence. There are two different name spaces, one for [types](03-types.html#types) @@ -72,8 +73,8 @@ In particular, imported names have higher precedence than names, defined in othe that might otherwise be visible because they are defined in either the current package or an enclosing package. -Note that a package definition is taken as lowest precedence, since packages -are open and can be defined across arbitrary compilation units. +Note that a binding introduced by a packaging is taken as lowest precedence, +since packages are open and can be defined across arbitrary compilation units. ```scala package util { @@ -85,42 +86,37 @@ package util { } ``` -The compiler supplies imports in a preamble to every source file. This preamble -conceptually has the following form, where braces indicate nested scopes: +The compiler supplies bindings from well-known packages and objects, called "root contexts". +The standard locations for these bindings are: -```scala -import java.lang._ -{ - import scala._ - { - import Predef._ - { /* source */ } - } -} -``` +1. The object `scala.Predef`. +1. The package `scala`. +1. The package `java.lang`. -These imports are taken as lowest precedence, so that they are always shadowed +These bindings are taken as lowest precedence, so that they are always shadowed by user code, which may contain competing imports and definitions. -They also increase the nesting depth as shown, so that later imports -shadow earlier ones. -As a convenience, multiple bindings of a type identifier to the same -underlying type is permitted. This is possible when import clauses introduce -a binding of a member type alias with the same binding precedence, typically -through wildcard imports. This allows redundant type aliases to be imported -without introducing an ambiguity. +A binding is available from a root context if it would also be available +using an ordinary import clause. In particular, ordinary access restrictions apply. + +A binding from an earlier root context shadows a binding of the same name from a later one. +For example, `scala.Predef.String` shadows `java.lang.String`, for which it is a type alias. + +Multiple binding of a type identifier to the same underlying type is permitted. +This is possible when import clauses introduce a binding of a member type alias +with the same binding precedence, typically through wildcard imports. +This allows redundant type aliases to be imported without introducing an ambiguity. ```scala object X { type T = annotation.tailrec } object Y { type T = annotation.tailrec } object Z { - import X._, Y._, annotation.{tailrec => T} // OK, all T mean tailrec - @T def f: Int = { f ; 42 } // error, f is not tail recursive + import X._, Y._ // OK, both T mean tailrec + @T def f: Int = { f ; 42 } // the annotation worked: error, f is not tail recursive } ``` -Similarly, imported aliases of names introduced by package statements are -allowed, even though the names are strictly ambiguous: +Similarly, imported aliases of names introduced by package statements are permitted: ```scala // c.scala @@ -128,16 +124,9 @@ package p { class C } // xy.scala import p._ -package p { class X extends C } -package q { class Y extends C } +package p { class X extends C } // not ambiguous (compiles without the import) +package q { class Y extends C } // requires the import ``` - -The reference to `C` in the definition of `X` is strictly ambiguous -because `C` is available by virtue of the package clause in -a different file, and can't shadow the imported name. But because -the references are the same, the definition is taken as though it -did shadow the import. - ###### Example Assume the following two definitions of objects named `X` in packages `p` and `q` diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 381b468aaa8b..6ed962a9d452 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1249,25 +1249,6 @@ trait Contexts { self: Analyzer => || unit.exists && s.sourceFile != unit.source.file) ) - - /** Does the import just import the defined symbol? - * - * `import p._ ; package p { S }` where `p.S` is defined elsewhere. - * `S` is both made available in `p` and imported, an ambiguity. - * (The import is not used and is extraneous, but normally a definition - * in `p` would shadow and result in maybe a warning, not an error.) - * - * Don't attempt to interfere with correctness everywhere. - * `object X { def f = ??? ; def g = { import X.f ; f } }` - * - * This method doesn't use the ImportInfo, `imp1`. - */ - private[Contexts] def reconcileAmbiguousImportAndDef(name: Name, impSym: Symbol, defSym: Symbol): Boolean = { - val res = impSym == defSym - if (res) log(s"Suppressing ambiguous import, taking $defSym for $name") - res - } - /** If the given import is permitted, fetch the symbol and filter for accessibility. */ private[Contexts] def importedAccessibleSymbol(imp: ImportInfo, sym: => Symbol): Symbol = @@ -1543,6 +1524,12 @@ trait Contexts { self: Analyzer => advanceCursorToNextImport() val preferDef: Boolean = defSym.exists && (!impSym.exists || { + // Does the import just import the defined symbol? + def reconcileAmbiguousImportAndDef: Boolean = { + val res = impSym == defSym + if (res) log(s"Suppressing ambiguous import, taking $defSym for $name") + res + } // 4) root imported symbols have same (lowest) precedence as package-owned symbols in different compilation units. if (imp1.depth < symbolDepth && imp1.isRootImport && foreignDefined) true @@ -1555,8 +1542,8 @@ trait Contexts { self: Analyzer => // Defined symbols take precedence over erroneous imports. else if (impSym.isError || impSym.name == nme.CONSTRUCTOR) true - // Try to reconcile them before giving up, at least if the def is not visible - else if (foreignDefined && thisContext.reconcileAmbiguousImportAndDef(name, impSym, defSym)) + // Try to reconcile them before giving up + else if (foreignDefined && reconcileAmbiguousImportAndDef) true // Otherwise they are irreconcilably ambiguous else diff --git a/test/files/neg/t12513.check b/test/files/neg/t12513.check new file mode 100644 index 000000000000..e156ba7fc383 --- /dev/null +++ b/test/files/neg/t12513.check @@ -0,0 +1,4 @@ +predefer_2.scala:10: error: not found: value x + println((x,y)) + ^ +1 error diff --git a/test/files/neg/t12513/predefer_2.scala b/test/files/neg/t12513/predefer_2.scala new file mode 100644 index 000000000000..6688ec5acbad --- /dev/null +++ b/test/files/neg/t12513/predefer_2.scala @@ -0,0 +1,12 @@ +// scalac: -Yimports:p.MyPredef,scala.Predef,scala + +package p { + object Test extends App { + println((x,y)) + } +} +package q { + object Test extends App { + println((x,y)) + } +} diff --git a/test/files/neg/t12513/predefined_1.scala b/test/files/neg/t12513/predefined_1.scala new file mode 100644 index 000000000000..eefdfa7011eb --- /dev/null +++ b/test/files/neg/t12513/predefined_1.scala @@ -0,0 +1,7 @@ + +package p + +object MyPredef { + private [p] def x = 27 + def y = 42 +} diff --git a/test/files/neg/t12513b.check b/test/files/neg/t12513b.check new file mode 100644 index 000000000000..6204fe160453 --- /dev/null +++ b/test/files/neg/t12513b.check @@ -0,0 +1,4 @@ +t12513b.scala:8: error: could not optimize @tailrec annotated method f: it contains a recursive call not in tail position + @T def f: Int = { f ; 42 } // the annotation worked: error, f is not tail recursive + ^ +1 error diff --git a/test/files/neg/t12513b.scala b/test/files/neg/t12513b.scala new file mode 100644 index 000000000000..51038b82a06d --- /dev/null +++ b/test/files/neg/t12513b.scala @@ -0,0 +1,9 @@ + +// scalac: -Werror -Xsource:3 + +object X { type T = annotation.tailrec } +object Y { type T = annotation.tailrec } +object Z { + import X.*, Y.* // OK, both T mean tailrec + @T def f: Int = { f ; 42 } // the annotation worked: error, f is not tail recursive +} diff --git a/test/files/pos/t12513c/c.scala b/test/files/pos/t12513c/c.scala new file mode 100644 index 000000000000..927c87347983 --- /dev/null +++ b/test/files/pos/t12513c/c.scala @@ -0,0 +1 @@ +package p { class C } diff --git a/test/files/pos/t12513c/xy.scala b/test/files/pos/t12513c/xy.scala new file mode 100644 index 000000000000..d46ea3f556a8 --- /dev/null +++ b/test/files/pos/t12513c/xy.scala @@ -0,0 +1,3 @@ +import p._ +package p { class X extends C } // not ambiguous (compiles without the import) +package q { class Y extends C } // requires the import From 25d0c2fdd422835f8b63bddbb9df568447fd7cfc Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 4 Oct 2022 13:09:25 +1000 Subject: [PATCH 024/261] Preserve tree attachment in the typer's Block/Apply inversion transform This keeps NamedApplyInfo attachment in place for downstream analysis by compiler plugins. --- .../scala/tools/nsc/typechecker/Typers.scala | 6 ++- .../nsc/typechecker/TreeAttachmentTest.scala | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7f19c31dff6c..8cb3c3e9a437 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4919,8 +4919,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } def typedApply(tree: Apply) = tree match { - case Apply(Block(stats, expr), args) => - typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) + case Apply(blk @ Block(stats, expr), args) => + val ap1 = treeCopy.Apply(tree, expr, args).clearType().setPos(tree.pos.makeTransparent) + val blk1 = treeCopy.Block(blk, stats, ap1).clearType() + typed1(blk1, mode, pt) case Apply(fun, args) => normalTypedApply(tree, fun, args) match { case treeInfo.ArrayInstantiation(level, componentType, arg) => diff --git a/test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala b/test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala new file mode 100644 index 000000000000..a58b18c8302b --- /dev/null +++ b/test/junit/scala/tools/nsc/typechecker/TreeAttachmentTest.scala @@ -0,0 +1,46 @@ +package scala.tools.nsc.typechecker + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.BytecodeTesting + +@RunWith(classOf[JUnit4]) +class TreeAttachmentTest extends BytecodeTesting { + + import compiler.global._ + + override def compilerArgs: String = "-Ystop-after:typer" + + @Test + def namedApplyInfoAttachmentPreserved(): Unit = { + def compile(testMethodCode: String, call: String): Option[analyzer.NamedApplyInfo] = { + val code = + s""" + |class C { + | $testMethodCode + | def s = "" + | def useTest = $call + |} + |""".stripMargin + + compiler.compileClasses(code) + val run = compiler.newRun + run.compileUnits(newCompilationUnit(code) :: Nil, run.parserPhase) + val tree : Tree = run.units.next.body + val block: Tree = tree.collect { case b: Block => b }.last + block.attachments.get[analyzer.NamedApplyInfo] + } + + + def vargss(x: Option[analyzer.NamedApplyInfo]): List[List[String]] = x.map(x => mmap(x.vargss)(_.symbol.name.toString)).getOrElse(Nil) + + assertEquals(None, compile("def test(a: Any, b: Any)(c: Any)(implicit d: DummyImplicit) = 0", "test(s, s)(s)")) + + val expected = List(List("x$2", "x$1"), List("x$3")) + assertEquals(expected, vargss(compile("def test(a: Any, b: Any)(c: Any) = 0", "test(b = s, a = s)(s)"))) + assertEquals(expected, vargss(compile("def test(a: Any, b: Any)(c: Any)(implicit d: DummyImplicit) = 0", "test(b = s, a = s)(s)"))) + } +} From a4d9229f7e0d664859e0d746df77c10408aa709f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 4 Oct 2022 16:27:20 -0700 Subject: [PATCH 025/261] Thread.getId is deprecated in JVM19 --- src/compiler/scala/tools/nsc/PipelineMain.scala | 1 + src/compiler/scala/tools/nsc/profile/Profiler.scala | 2 ++ src/library/scala/concurrent/impl/ExecutionContextImpl.scala | 1 + src/reflect/scala/reflect/internal/util/ChromeTrace.scala | 1 + src/testkit/scala/tools/testkit/AllocationTest.scala | 1 + 5 files changed, 6 insertions(+) diff --git a/src/compiler/scala/tools/nsc/PipelineMain.scala b/src/compiler/scala/tools/nsc/PipelineMain.scala index 06adc05f8cc6..e6f0940d196c 100644 --- a/src/compiler/scala/tools/nsc/PipelineMain.scala +++ b/src/compiler/scala/tools/nsc/PipelineMain.scala @@ -325,6 +325,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe trace.append("""{"traceEvents": [""") val sb = new mutable.StringBuilder(trace) + @annotation.nowarn("cat=deprecation") def durationEvent(name: String, cat: String, t: Timer): String = { s"""{"name": "$name", "cat": "$cat", "ph": "X", "ts": ${(t.startMicros).toLong}, "dur": ${(t.durationMicros).toLong}, "pid": 0, "tid": ${t.thread.getId}}""" } diff --git a/src/compiler/scala/tools/nsc/profile/Profiler.scala b/src/compiler/scala/tools/nsc/profile/Profiler.scala index 9a50c0101940..9b9e67e1a965 100644 --- a/src/compiler/scala/tools/nsc/profile/Profiler.scala +++ b/src/compiler/scala/tools/nsc/profile/Profiler.scala @@ -129,6 +129,7 @@ private [profile] object RealProfiler { private val idGen = new AtomicInteger() lazy val allPlugins = ServiceLoader.load(classOf[ProfilerPlugin]).iterator.asScala.toList + @annotation.nowarn("cat=deprecation") private[profile] def snapThread(idleTimeNanos: Long): ProfileSnap = { val current = Thread.currentThread() val allocatedBytes = threadMx.getThreadAllocatedBytes(Thread.currentThread().getId) @@ -403,6 +404,7 @@ class StreamProfileReporter(out:PrintWriter) extends ProfileReporter { override def reportForeground(profiler: RealProfiler, threadRange: ProfileRange): Unit = { reportCommon(EventType.MAIN, profiler, threadRange) } + @annotation.nowarn("cat=deprecation") private def reportCommon(tpe:EventType.value, profiler: RealProfiler, threadRange: ProfileRange): Unit = { out.println(s"$tpe,${threadRange.start.snapTimeNanos},${threadRange.end.snapTimeNanos},${profiler.id},${threadRange.phase.id},${threadRange.phase.name},${threadRange.purpose},${threadRange.taskCount},${threadRange.thread.getId},${threadRange.thread.getName},${threadRange.runNs},${threadRange.idleNs},${threadRange.cpuNs},${threadRange.userNs},${threadRange.allocatedBytes},${threadRange.end.heapBytes} ") } diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index e59e723e8752..d662df8927a8 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -35,6 +35,7 @@ private[concurrent] object ExecutionContextImpl { private final val blockerPermits = new Semaphore(maxBlockers) + @annotation.nowarn("cat=deprecation") def wire[T <: Thread](thread: T): T = { thread.setDaemon(daemonic) thread.setUncaughtExceptionHandler(uncaught) diff --git a/src/reflect/scala/reflect/internal/util/ChromeTrace.scala b/src/reflect/scala/reflect/internal/util/ChromeTrace.scala index 8b3c533648ed..d77bb46fa876 100644 --- a/src/reflect/scala/reflect/internal/util/ChromeTrace.scala +++ b/src/reflect/scala/reflect/internal/util/ChromeTrace.scala @@ -43,6 +43,7 @@ final class ChromeTrace(f: Path) extends Closeable { private val traceWriter = FileUtils.newAsyncBufferedWriter(f) private val context = mutable.Stack[JsonContext](TopContext) private val tidCache = new ThreadLocal[String]() { + @annotation.nowarn("cat=deprecation") override def initialValue(): String = f"${Thread.currentThread().getId}%05d" } objStart() diff --git a/src/testkit/scala/tools/testkit/AllocationTest.scala b/src/testkit/scala/tools/testkit/AllocationTest.scala index 37b943215fd7..85d9bbd54edf 100644 --- a/src/testkit/scala/tools/testkit/AllocationTest.scala +++ b/src/testkit/scala/tools/testkit/AllocationTest.scala @@ -132,6 +132,7 @@ trait AllocationTest { private[AllocationTest] def calcAllocationInfo[T](fn: => T, cost: Long, text: String, ignoreEqualCheck: Boolean)(implicit execution: AllocationExecution = AllocationExecution()): AllocationInfo[T] = { val expected = fn val extraText = if (text.isEmpty) "" else s" -- $text" + @annotation.nowarn("cat=deprecation") val id = Thread.currentThread().getId val counts = new Array[Long](execution.executionCount) From ace81da2b6f72e5665b3b842f25bd8f3aa34f818 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Sat, 8 Oct 2022 15:46:35 -0300 Subject: [PATCH 026/261] new reference compiler is Scala 2.13.10 --- build.sbt | 2 +- project/MimaFilters.scala | 2 +- versions.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 9d497effc816..8566c39aee7c 100644 --- a/build.sbt +++ b/build.sbt @@ -73,7 +73,7 @@ lazy val publishSettings : Seq[Setting[_]] = Seq( // should not be set directly. It is the same as the Maven version and derived automatically from `baseVersion` and // `baseVersionSuffix`. globalVersionSettings -Global / baseVersion := "2.13.10" +Global / baseVersion := "2.13.11" Global / baseVersionSuffix := "SNAPSHOT" ThisBuild / organization := "org.scala-lang" ThisBuild / homepage := Some(url("https://www.scala-lang.org")) diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index bc5b70009729..cdc43bacc57c 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -13,7 +13,7 @@ object MimaFilters extends AutoPlugin { import autoImport._ override val globalSettings = Seq( - mimaReferenceVersion := Some("2.13.9"), + mimaReferenceVersion := Some("2.13.10"), ) val mimaFilters: Seq[ProblemFilter] = Seq[ProblemFilter]( diff --git a/versions.properties b/versions.properties index c049b91d2833..4b2572e1c19a 100644 --- a/versions.properties +++ b/versions.properties @@ -1,5 +1,5 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.13.9 +starr.version=2.13.10 # These are the versions of the modules that go with this release. # Artifact dependencies: From cc11e8ed7c2293a5afaa900cb743bc568bbb6b4d Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 27 Sep 2022 15:17:59 +0200 Subject: [PATCH 027/261] Allow eta-expansion of methods with dependent types --- .../tools/nsc/typechecker/ContextErrors.scala | 5 -- .../tools/nsc/typechecker/EtaExpansion.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 9 +-- .../scala/reflect/internal/Definitions.scala | 2 +- ...pendentMethodTpeConversionToFunction.check | 4 -- ...pendentMethodTpeConversionToFunction.scala | 5 -- test/files/run/eta-dependent.check | 48 +++++++++++++ test/files/run/eta-dependent.scala | 72 +++++++++++++++++++ 8 files changed, 125 insertions(+), 22 deletions(-) delete mode 100644 test/files/neg/error_dependentMethodTpeConversionToFunction.check delete mode 100644 test/files/neg/error_dependentMethodTpeConversionToFunction.scala create mode 100644 test/files/run/eta-dependent.check create mode 100644 test/files/run/eta-dependent.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2521ce0b4c40..29c1f0f24bd5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -834,11 +834,6 @@ trait ContextErrors extends splain.SplainErrors { setError(tree) } - def DependentMethodTpeConversionToFunctionError(tree: Tree, tp: Type): Tree = { - issueNormalTypeError(tree, "method with dependent type " + tp + " cannot be converted to function value") - setError(tree) - } - // cases where we do not necessarily return trees //checkStarPatOK diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index d48ca53570c8..439aedac1c26 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -24,7 +24,7 @@ import symtab.Flags._ trait EtaExpansion { self: Analyzer => import global._ - /** Expand partial method application `p.f(es_1)...(es_n)`. Does not support dependent method types (yet). + /** Expand partial method application `p.f(es_1)...(es_n)`. * * We expand this to the following block, which evaluates * the target of the application and its supplied arguments if needed (they are not stable), diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e1eed2b88345..d592809fcb93 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3202,12 +3202,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def typedEtaExpansion(tree: Tree, mode: Mode, pt: Type): Tree = { debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt") - if (tree.tpe.isDependentMethodType) DependentMethodTpeConversionToFunctionError(tree, tree.tpe) // TODO: support this - else { - val expansion = etaExpand(tree, context.owner) - if (context.undetparams.isEmpty) typed(expansion, mode, pt) - else instantiate(typed(expansion, mode), mode, pt) - } + val expansion = etaExpand(tree, context.owner) + if (context.undetparams.isEmpty) typed(expansion, mode, pt) + else instantiate(typed(expansion, mode), mode, pt) } def typedRefinement(templ: Template): Unit = { diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index ef7a73c87258..44ffcc8e926b 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -855,7 +855,7 @@ trait Definitions extends api.StandardDefinitions { | was: $restpe | now""")(methodToExpressionTp(restpe)) case mt @ MethodType(_, restpe) if mt.isImplicit => methodToExpressionTp(restpe) - case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => + case mt @ MethodType(_, restpe) => if (phase.erasedTypes) FunctionClass(mt.params.length).tpe else functionType(mt.paramTypes, methodToExpressionTp(restpe)) case NullaryMethodType(restpe) => methodToExpressionTp(restpe) diff --git a/test/files/neg/error_dependentMethodTpeConversionToFunction.check b/test/files/neg/error_dependentMethodTpeConversionToFunction.check deleted file mode 100644 index 58da78fe9452..000000000000 --- a/test/files/neg/error_dependentMethodTpeConversionToFunction.check +++ /dev/null @@ -1,4 +0,0 @@ -error_dependentMethodTpeConversionToFunction.scala:4: error: method with dependent type (x: AnyRef): x.type cannot be converted to function value - val x: Any => Any = foo - ^ -1 error diff --git a/test/files/neg/error_dependentMethodTpeConversionToFunction.scala b/test/files/neg/error_dependentMethodTpeConversionToFunction.scala deleted file mode 100644 index d0c4cb48a8e0..000000000000 --- a/test/files/neg/error_dependentMethodTpeConversionToFunction.scala +++ /dev/null @@ -1,5 +0,0 @@ -// test DependentMethodTpeConversionToFunctionError -object Test { - def foo(x: AnyRef): x.type = x - val x: Any => Any = foo -} diff --git a/test/files/run/eta-dependent.check b/test/files/run/eta-dependent.check new file mode 100644 index 000000000000..4d21f8dd08c4 --- /dev/null +++ b/test/files/run/eta-dependent.check @@ -0,0 +1,48 @@ + +scala> object defs { + val a = "obj" + def aa: a.type = a + def s = this + def f(x: Int): a.type = a + def g(x: Int)(y: x.type) = 0 + def h(x: a.type)(y: a.type) = 0 +} +object defs + +scala> import defs._ +import defs._ + +scala> val f1 = f _ +val f1: Int => defs.a.type = + +scala> val f2: Int => a.type = f +val f2: Int => defs.a.type = + +scala> val f3: Int => Object = f +val f3: Int => Object = + +scala> val g1 = g(10) _ +val g1: Int(10) => Int = + +scala> val g2: 10 => Int = g1 +val g2: 10 => Int = + +scala> val g3: 11 => Int = g(11) +val g3: 11 => Int = + +scala> val g4: Int => Int = g(11) // mismatch + ^ + error: type mismatch; + found : Int(11) => Int + required: Int => Int + +scala> val h1 = s.h(aa) _ +val h1: defs.a.type => Int = + +scala> val h2: a.type => Int = h1 +val h2: defs.a.type => Int = + +scala> val h3: a.type => Int = s.h(aa) +val h3: defs.a.type => Int = + +scala> :quit diff --git a/test/files/run/eta-dependent.scala b/test/files/run/eta-dependent.scala new file mode 100644 index 000000000000..1ceb5957cfd3 --- /dev/null +++ b/test/files/run/eta-dependent.scala @@ -0,0 +1,72 @@ +object NoMoreNeg { + def foo(x: AnyRef): x.type = x + val x: AnyRef => Any = foo +} + +object t12641 { + def f(sb: StringBuilder) = Option("").foreach(sb.append) +} + +object t12641a { + trait A { + def foo(s: String): this.type + def foo(s: Int): this.type + } + trait T { + val a1: A + val o: Option[String] + + def t(a2: A): Unit = { + o.foreach(a1.foo) + o.foreach(a2.foo) + + val f2: String => a2.type = a2.foo + val f3: String => A = a2.foo + } + } +} + +object t12641b { + trait A { + def foo(s: String): this.type + } + trait T { + val a1: A + val o: Option[String] + + def t(a2: A): Unit = { + o.foreach(a1.foo) + o.foreach(a2.foo) + + val f1 = a2.foo _ + val f2: String => a2.type = a2.foo + val f3: String => A = a2.foo + } + } +} + +import scala.tools.partest._ + +object Test extends ReplTest with Lambdaless { + def code = """ +object defs { + val a = "obj" + def aa: a.type = a + def s = this + def f(x: Int): a.type = a + def g(x: Int)(y: x.type) = 0 + def h(x: a.type)(y: a.type) = 0 +} +import defs._ +val f1 = f _ +val f2: Int => a.type = f +val f3: Int => Object = f +val g1 = g(10) _ +val g2: 10 => Int = g1 +val g3: 11 => Int = g(11) +val g4: Int => Int = g(11) // mismatch +val h1 = s.h(aa) _ +val h2: a.type => Int = h1 +val h3: a.type => Int = s.h(aa) +""".trim +} From 82ddcd6a67b94a191a34f73a96925ba5ce78ed67 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 11 Oct 2022 00:49:42 -0700 Subject: [PATCH 028/261] Tweak t12201 to run unoptimized --- test/files/instrumented/t12201.check | 3 --- test/files/instrumented/t12201.scala | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 test/files/instrumented/t12201.check diff --git a/test/files/instrumented/t12201.check b/test/files/instrumented/t12201.check deleted file mode 100644 index ba4c268ba7ac..000000000000 --- a/test/files/instrumented/t12201.check +++ /dev/null @@ -1,3 +0,0 @@ -Method call statistics: - 1 scala/runtime/BoxedUnit.()V - 1 scala/runtime/BoxedUnit.()V diff --git a/test/files/instrumented/t12201.scala b/test/files/instrumented/t12201.scala index a5a1d1860bdb..f38ae90f55c7 100644 --- a/test/files/instrumented/t12201.scala +++ b/test/files/instrumented/t12201.scala @@ -1,7 +1,10 @@ import scala.tools.partest.instrumented.Instrumentation._ object Test { + @noinline def discard(x: Any) = () + def main(args: Array[String]): Unit = { + discard((): Any) // ensure BoxedUnit is loaded; only under -opt is it not loaded before this method startProfiling() // to optimized @@ -24,6 +27,6 @@ object Test { val k = Array[Unit](()) stopProfiling() - printStatistics() + assert(getStatistics.isEmpty) } } From d53e251802685196aa57590816819294e40eb939 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 13 Oct 2022 09:35:04 -0300 Subject: [PATCH 029/261] mailmap: condense Liang Yan entries --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index 815b48ad3c20..11c35d09e976 100644 --- a/.mailmap +++ b/.mailmap @@ -45,6 +45,8 @@ Josh Suereth Josh Suereth Julien Eberle Kenji Yoshida <6b656e6a69@gmail.com> +Liang Yan <35164941+liang3zy22@users.noreply.github.com> +Liang Yan Luc Bourlier Luc Bourlier Luc Bourlier From 8ecb219fa285e12ff2b3236c8a082937696900c3 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 12 Oct 2022 16:38:38 -0700 Subject: [PATCH 030/261] Ignore override type just for whitebox expansion --- .../scala/tools/nsc/typechecker/Namers.scala | 24 +++++++++++++++++-- test/files/neg/t12647.check | 4 ++++ test/files/neg/t12647/Macro_1.scala | 13 ++++++++++ test/files/neg/t12647/Resolve_2.scala | 13 ++++++++++ test/files/neg/t12647/Test_3.scala | 7 ++++++ test/files/pos/t12647/Macro_1.scala | 13 ++++++++++ test/files/pos/t12647/Resolve_2.scala | 13 ++++++++++ test/files/pos/t12647/Test_3.scala | 7 ++++++ test/files/pos/t12647b/Macro_1.scala | 11 +++++++++ test/files/pos/t12647b/Resolve_2.scala | 11 +++++++++ test/files/pos/t12647b/Test_3.scala | 5 ++++ 11 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t12647.check create mode 100644 test/files/neg/t12647/Macro_1.scala create mode 100644 test/files/neg/t12647/Resolve_2.scala create mode 100644 test/files/neg/t12647/Test_3.scala create mode 100644 test/files/pos/t12647/Macro_1.scala create mode 100644 test/files/pos/t12647/Resolve_2.scala create mode 100644 test/files/pos/t12647/Test_3.scala create mode 100644 test/files/pos/t12647b/Macro_1.scala create mode 100644 test/files/pos/t12647b/Resolve_2.scala create mode 100644 test/files/pos/t12647b/Test_3.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 89b110ab2ece..74f6f34dea1a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1120,14 +1120,34 @@ trait Namers extends MethodSynthesis { /** Computes the type of the body in a ValDef or DefDef, and * assigns the type to the tpt's node. Returns the type. + * + * Under `-Xsource:3`, use `pt`, the type of the overridden member. + * But preserve the precise type of a whitebox macro. + * For `def f = macro g`, here we see `def f = xp(g)` the expansion, + * not the `isMacro` case: `openMacros` will be nonEmpty. + * For `def m = f`, retrieve the typed RHS and check if it is an expansion; + * in that case, check if the expandee `f` is whitebox and preserve + * the precise type if it is. The user must provide an explicit type + * to "opt out" of the inferred narrow type; in Scala 3, they would + * inline the def to "opt in". */ private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = { val rhsTpe = tree match { - case ddef: DefDef if tree.symbol.isTermMacro => defnTyper.computeMacroDefType(ddef, pt) + case ddef: DefDef if tree.symbol.isTermMacro => defnTyper.computeMacroDefType(ddef, pt) // unreached, see methodSig case _ => defnTyper.computeType(tree.rhs, pt) } tree.tpt.defineType { - if (currentRun.isScala3 && !pt.isWildcard && pt != NoType && !pt.isErroneous && openMacros.isEmpty) pt + val inferOverridden = currentRun.isScala3 && + !pt.isWildcard && pt != NoType && !pt.isErroneous && + openMacros.isEmpty && { + context.unit.transformed.get(tree.rhs) match { + case Some(t) if t.hasAttachment[MacroExpansionAttachment] => + val xp = macroExpandee(t) + xp.symbol == null || isBlackbox(xp.symbol) + case _ => true + } + } + if (inferOverridden) pt else dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt)) }.setPos(tree.pos.focus) tree.tpt.tpe diff --git a/test/files/neg/t12647.check b/test/files/neg/t12647.check new file mode 100644 index 000000000000..7b0f04dc8b10 --- /dev/null +++ b/test/files/neg/t12647.check @@ -0,0 +1,4 @@ +Test_3.scala:6: error: value value is not a member of Result + println(resolver.resolve.value) + ^ +1 error diff --git a/test/files/neg/t12647/Macro_1.scala b/test/files/neg/t12647/Macro_1.scala new file mode 100644 index 000000000000..748657682eec --- /dev/null +++ b/test/files/neg/t12647/Macro_1.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import scala.reflect.macros.blackbox.Context + +trait Result + +object Macros { + def impl(c: Context) = { + import c.universe._ + q"""new Result { def value = "Was this the answer you sought?" }""" + } +} diff --git a/test/files/neg/t12647/Resolve_2.scala b/test/files/neg/t12647/Resolve_2.scala new file mode 100644 index 000000000000..dab65c6a310b --- /dev/null +++ b/test/files/neg/t12647/Resolve_2.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import language.experimental.macros + +trait Resolver { + def resolve: Result = ??? +} + +class ValueResolver extends Resolver { + override def resolve = valueResult + def valueResult: Result = macro Macros.impl +} diff --git a/test/files/neg/t12647/Test_3.scala b/test/files/neg/t12647/Test_3.scala new file mode 100644 index 000000000000..152e5ddc4aa4 --- /dev/null +++ b/test/files/neg/t12647/Test_3.scala @@ -0,0 +1,7 @@ + +// scalac: -Xsource:3 + +object Test extends App { + val resolver = new ValueResolver + println(resolver.resolve.value) +} diff --git a/test/files/pos/t12647/Macro_1.scala b/test/files/pos/t12647/Macro_1.scala new file mode 100644 index 000000000000..a808e46089a3 --- /dev/null +++ b/test/files/pos/t12647/Macro_1.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import scala.reflect.macros.whitebox.Context + +trait Result + +object Macros { + def impl(c: Context) = { + import c.universe._ + q"""new Result { def value = "Was this the answer you sought?" }""" + } +} diff --git a/test/files/pos/t12647/Resolve_2.scala b/test/files/pos/t12647/Resolve_2.scala new file mode 100644 index 000000000000..dab65c6a310b --- /dev/null +++ b/test/files/pos/t12647/Resolve_2.scala @@ -0,0 +1,13 @@ + +// scalac: -Xsource:3 + +import language.experimental.macros + +trait Resolver { + def resolve: Result = ??? +} + +class ValueResolver extends Resolver { + override def resolve = valueResult + def valueResult: Result = macro Macros.impl +} diff --git a/test/files/pos/t12647/Test_3.scala b/test/files/pos/t12647/Test_3.scala new file mode 100644 index 000000000000..152e5ddc4aa4 --- /dev/null +++ b/test/files/pos/t12647/Test_3.scala @@ -0,0 +1,7 @@ + +// scalac: -Xsource:3 + +object Test extends App { + val resolver = new ValueResolver + println(resolver.resolve.value) +} diff --git a/test/files/pos/t12647b/Macro_1.scala b/test/files/pos/t12647b/Macro_1.scala new file mode 100644 index 000000000000..e54621c987e4 --- /dev/null +++ b/test/files/pos/t12647b/Macro_1.scala @@ -0,0 +1,11 @@ + +import scala.reflect.macros.whitebox.Context + +trait Result + +object Macros { + def impl(c: Context): c.Tree = { + import c.universe._ + q"""new Result { def value = "Was this the answer you sought?" }""" + } +} diff --git a/test/files/pos/t12647b/Resolve_2.scala b/test/files/pos/t12647b/Resolve_2.scala new file mode 100644 index 000000000000..cbf1457b8635 --- /dev/null +++ b/test/files/pos/t12647b/Resolve_2.scala @@ -0,0 +1,11 @@ + +import language.experimental.macros + +abstract class Resolver { + def resolve: Result = ??? +} + +class ValueResolver extends Resolver { + override def resolve = valueResult + def valueResult: Result = macro Macros.impl +} diff --git a/test/files/pos/t12647b/Test_3.scala b/test/files/pos/t12647b/Test_3.scala new file mode 100644 index 000000000000..16149d9965bc --- /dev/null +++ b/test/files/pos/t12647b/Test_3.scala @@ -0,0 +1,5 @@ + +object Test extends App { + val resolver = new ValueResolver + println(resolver.resolve.value) +} From 8b7f3d7eb6fba3e0bbf7c3556c2b51082badc4ce Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sat, 15 Oct 2022 09:42:25 +0000 Subject: [PATCH 031/261] Update sbt to 1.7.2 in 2.12.x --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 22af2628c413..563a014da4aa 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.1 +sbt.version=1.7.2 From d81bae12d0f3bbb877ff9c5ea5a138d08d1654d2 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 15 Oct 2022 19:21:38 -0700 Subject: [PATCH 032/261] Tweak compatible implicit check for refined type --- .../tools/nsc/typechecker/Implicits.scala | 22 ++++++------- test/files/pos/t12666.scala | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 test/files/pos/t12666.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 389a77902a9b..71abc0a846df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -741,23 +741,14 @@ trait Implicits extends splain.SplainData { as = as.tail } } else { - while (ps.nonEmpty && as.nonEmpty) { + while (!(ps.isEmpty || as.isEmpty)) { if (!(as.head <:< ps.head.tpe)) return false ps = ps.tail as = as.tail } } - ps.isEmpty && as.nonEmpty && { - val lastArg = as.head - as.tail.isEmpty && { - lastArg match { - case RefinedType(_, syms) if !syms.isEmpty => - syms.reverseIterator.exists(x => context.isAccessible(restpe.nonPrivateMember(x.name), restpe)) - case _ => true - } - } && loop(restpe, lastArg) - } + ps.isEmpty && !as.isEmpty && as.tail.isEmpty && loop(restpe, as.head) } } @@ -766,7 +757,14 @@ trait Implicits extends splain.SplainData { case NullaryMethodType(restpe) => loop(restpe, pt) case PolyType(_, restpe) => loop(restpe, pt) case ExistentialType(_, qtpe) => if (fast) loop(qtpe, pt) else methodToExpressionTp(tp) <:< pt // is !fast case needed?? - case _ => if (fast) isPlausiblySubType(tp, pt) else tp <:< pt + case _ => (if (fast) isPlausiblySubType(tp, pt) else tp <:< pt) && { + pt match { + case RefinedType(_, syms) if !syms.isEmpty => + syms.reverseIterator.exists(x => context.isAccessible(tp.nonPrivateMember(x.name), tp)) + case _ => + true + } + } } loop(tp0, pt0) } diff --git a/test/files/pos/t12666.scala b/test/files/pos/t12666.scala new file mode 100644 index 000000000000..33d336b3224a --- /dev/null +++ b/test/files/pos/t12666.scala @@ -0,0 +1,32 @@ + +package foo { + + trait Baz[A] + object Baz { + implicit def instance[A]: Baz[A] = ??? + } + + package syntax { + object all { + implicit def ops1[A: Baz](a: A): BarOps1 = new BarOps1(a) + implicit def ops2[A: Baz](a: A): BarOps2 = new BarOps2(a) + } + + class BarOps1(val a: Any) extends AnyVal { + def bar(x: Int): String = ??? + } + + class BarOps2(val a: Any) extends AnyVal { + private[syntax] def bar(x: Int): String = ??? + } + } +} + +import foo.syntax.all._ + +object Main { + def main(args: Array[String]): Unit = { + val a = new Object + a.bar(42) + } +} From 0b67d570f0c6b62a549af44616e6b5bf3940a18f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 6 May 2021 10:46:59 -0700 Subject: [PATCH 033/261] Test REPL valueOfTerm --- test/files/run/t12390.scala | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/files/run/t12390.scala diff --git a/test/files/run/t12390.scala b/test/files/run/t12390.scala new file mode 100644 index 000000000000..73f2bb7286ad --- /dev/null +++ b/test/files/run/t12390.scala @@ -0,0 +1,45 @@ +import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter._, shell._ + +import java.io.{PrintWriter, OutputStream} + +object Test { + + def main(args: Array[String]): Unit = { + //val filename: String = ".../data/generated_01mb.base64" + val filename: String = sys.props("partest.test-path") + val code: String = + "import scala.io.Source\n " + + "Source.fromFile(\"" + filename + "\").getLines().mkString(\"\\n\")" + + val s = new Settings() + + s.processArguments( + List( + //"-Xprint:typer", + "-deprecation", + "-Yrepl-class-based", + "-Yrepl-outdir", "./target" + ), processAll = true) + + val drain = new OutputStream { override def write(byte: Int) = () } + val sinkWriter = new PrintWriter(drain) + val reporter = new ReplReporterImpl(ShellConfig(s), s, sinkWriter) + val repl = new IMain(s, reporter) + repl.settings.usejavacp.value = true + for(i <- 1 to 65) { + repl.interpret(code) match { + case Results.Success => + assert(repl.valueOfTerm(repl.mostRecentVar).get != null) // 2.12: null after 60 + case other => + throw new MatchError(other) + } + } + } +} +/* + JavaMirror.scalaSimpleName throws on the long class name and valueOfTerm ignores the error. + + scalaSimpleName expects the wrapper class name to have its enclosing class name as a prefix, + but some special encoding kicks in at 64 chars +*/ From 6c8c97b6f81c9c57e210a2149172a36acd9190f0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 1 Oct 2022 15:05:47 -0700 Subject: [PATCH 034/261] Accept -Vprint:all, -Vprint:~tailcalls --- src/compiler/scala/tools/nsc/Global.scala | 14 +++--- .../tools/nsc/settings/MutableSettings.scala | 15 ++++-- .../tools/nsc/settings/SettingsTest.scala | 48 +++++++++++++++++++ 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 926496d913fb..b63cdf08fdb7 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1275,26 +1275,26 @@ class Global(var currentSettings: Settings, reporter0: Reporter) // doesn't select a unique phase, that might be surprising too. def checkPhaseSettings(including: Boolean, specs: Seq[String]*) = { def isRange(s: String) = s.forall(c => c.isDigit || c == '-') - def isSpecial(s: String) = (s == "_" || isRange(s)) + def isMulti(s: String) = s == "_" || s == "all" || isRange(s) || s.startsWith("~") val tester = new ss.PhasesSetting("fake","fake") for (p <- specs.flatten.to(Set)) { tester.value = List(p) val count = if (including) first.iterator.count(tester.containsPhase(_)) - else phaseDescriptors.count(pd => tester.contains(pd.phaseName)) + else phaseDescriptors.count(pd => tester.contains(pd.phaseName) || tester.contains(s"~${pd.phaseName}")) if (count == 0) runReporting.warning(NoPosition, s"'$p' specifies no phase", WarningCategory.Other, site = "") - if (count > 1 && !isSpecial(p)) runReporting.warning(NoPosition, s"'$p' selects $count phases", WarningCategory.Other, site = "") - if (!including && isSpecial(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'") + if (count > 1 && !isMulti(p)) runReporting.warning(NoPosition, s"'$p' selects $count phases", WarningCategory.Other, site = "") + if (!including && isMulti(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'") tester.clear() } } // phases that are excluded; for historical reasons, these settings only select by phase name val exclusions = List(ss.stopBefore, ss.stopAfter, ss.skip) val inclusions = ss.visibleSettings collect { - case s: ss.PhasesSetting if !(exclusions contains s) => s.value + case s: ss.PhasesSetting if !exclusions.contains(s) => s.value } checkPhaseSettings(including = true, inclusions.toSeq: _*) - checkPhaseSettings(including = false, exclusions map (_.value): _*) + checkPhaseSettings(including = false, exclusions.map(_.value): _*) // Report the overhead of statistics measurements per every run if (settings.areStatisticsEnabled) @@ -1536,7 +1536,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) informTime(globalPhase.description, phaseTimer.nanos) // progress update - if ((settings.Xprint containsPhase globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { + if (settings.Xprint.containsPhase(globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { // print trees if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) nodePrinters.printAll() else printAllUnits() diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 314b5393138f..aa81abb28041 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -945,14 +945,19 @@ class MutableSettings(val errorFn: String => Unit, val pathFactory: PathFactory) def clear(): Unit = (v = Nil) - // we slightly abuse the usual meaning of "contains" here by returning - // true if our phase list contains "_", regardless of the incoming argument + /* True if the named phase is selected. + * + * A setting value "_" or "all" selects all phases by name. + */ def contains(phName: String) = doAllPhases || containsName(phName) - def containsName(phName: String) = stringValues exists (phName startsWith _) + /* True if the given phase name matches the selection, possibly as prefixed "~name". */ + def containsName(phName: String) = stringValues.exists(phName.startsWith(_)) def containsId(phaseId: Int) = phaseIdTest(phaseId) - def containsPhase(ph: Phase) = contains(ph.name) || containsId(ph.id) + /* True if the phase is selected by name or "all", or by id, or by prefixed "~name". */ + def containsPhase(ph: Phase) = contains(ph.name) || containsId(ph.id) || containsName(s"~${ph.name}") || + ph.next != null && containsName(s"~${ph.next.name}") // null if called during construction - def doAllPhases = stringValues.contains("_") + def doAllPhases = stringValues.exists(s => s == "_" || s == "all") def unparse: List[String] = value.map(v => s"$name:$v") withHelpSyntax( diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala index 3d63d7fe75c6..ce46108f09db 100644 --- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala +++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala @@ -354,6 +354,54 @@ class SettingsTest { assertFalse(s.isInfo) assertTrue(s.printArgs.isSetByUser) } + @Test def `name-based phases setting accepts tilde prefix`: Unit = { + val start = new Phase(null) { def name = "start"; def run() = () } + val chunk = new Phase(start) { def name = "chunker"; def run() = () } + val clean = new Phase(chunk) { def name = "clean"; def run() = () } + val go = new Phase(clean) { def name = "go"; def run() = () } + val end = new Phase(go) { def name = "end"; def run() = () } + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + val ps = new s.PhasesSetting("-Yp", descr="", default="") + s.allSettings(ps.name) = ps + val args = List("-Yp:clean,~chunker,3") + val (ok, residual) = s.processArguments(args, processAll = true) + assertTrue(ok) + assertTrue(residual.isEmpty) + assertTrue(ps.contains("clean")) + assertFalse(ps.contains("chunker")) + assertTrue(ps.contains("~chunker")) + assertFalse(ps.contains("start")) + assertFalse(ps.contains("end")) + assertTrue(ps.containsPhase(clean)) + assertTrue(ps.containsPhase(chunk)) + assertTrue(ps.containsPhase(start)) + assertTrue(ps.containsPhase(start.next)) + assertTrue(ps.contains(s"~${start.next.name}")) + assertTrue(ps.containsPhase(go)) + assertTrue(ps.containsId(go.id)) + assertFalse(ps.containsPhase(end)) + assertFalse(ps.containsId(end.id)) + } + @Test def `phases setting accepts all or underscore`: Unit = { + val start = new Phase(null) { def name = "start"; def run() = () } + def check(args: String*): MutableSettings#PhasesSetting = { + val s = new MutableSettings(msg => throw new IllegalArgumentException(msg)) + val ps = new s.PhasesSetting("-Yp", descr="", default="") + s.allSettings(ps.name) = ps + val (ok, residual) = s.processArguments(args.toList, processAll = true) + assertTrue(ok) + assertTrue(residual.isEmpty) + ps + } + assertTrue(check("-Yp:start").containsPhase(start)) + assertTrue(check("-Yp:start").contains("start")) + assertTrue(check("-Yp:start").containsName("start")) + assertTrue(check("-Yp:all").containsPhase(start)) + assertTrue(check("-Yp:all").contains("start")) + assertFalse(check("-Yp:all").containsName("start")) + assertTrue(check("-Yp:_").containsPhase(start)) + assertTrue(check("-Yp:junk,_").containsPhase(start)) + } } object SettingsTest { import language.implicitConversions From ced06c29285452139715d0e848ceb61dee2f509c Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 19 Oct 2022 14:10:37 -0700 Subject: [PATCH 035/261] Require element copies succeed --- .../scala/collection/mutable/ArrayBuffer.scala | 3 ++- .../scala/collection/mutable/ArrayBuilder.scala | 3 ++- .../scala/collection/mutable/ArrayDeque.scala | 3 ++- .../scala/collection/mutable/ArrayBufferTest.scala | 14 +++++++------- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala index bc402bd8a8e5..3d0f7385d72b 100644 --- a/src/library/scala/collection/mutable/ArrayBuffer.scala +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -197,7 +197,8 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int) // - `copyElemsToArray` will call `System.arraycopy` // - `System.arraycopy` will effectively "read" all the values before // overwriting any of them when two arrays are the the same reference - IterableOnce.copyElemsToArray(elems, array.asInstanceOf[Array[Any]], index, elemsLength) + val actual = IterableOnce.copyElemsToArray(elems, array.asInstanceOf[Array[Any]], index, elemsLength) + if (actual != elemsLength) throw new IllegalStateException(s"Copied $actual of $elemsLength") size0 = len + elemsLength // update size AFTER the copy, in case we're inserting a proxy } case _ => insertAll(index, ArrayBuffer.from(elems)) diff --git a/src/library/scala/collection/mutable/ArrayBuilder.scala b/src/library/scala/collection/mutable/ArrayBuilder.scala index 880744d8473f..454527bcdebd 100644 --- a/src/library/scala/collection/mutable/ArrayBuilder.scala +++ b/src/library/scala/collection/mutable/ArrayBuilder.scala @@ -61,7 +61,8 @@ sealed abstract class ArrayBuilder[T] val k = xs.knownSize if (k > 0) { ensureSize(this.size + k) - IterableOnce.copyElemsToArray(xs, elems, this.size) + val actual = IterableOnce.copyElemsToArray(xs, elems, this.size) + if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") size += k } else if (k < 0) super.addAll(xs) this diff --git a/src/library/scala/collection/mutable/ArrayDeque.scala b/src/library/scala/collection/mutable/ArrayDeque.scala index 1d8b9fe597e4..5dc4cbbaf83f 100644 --- a/src/library/scala/collection/mutable/ArrayDeque.scala +++ b/src/library/scala/collection/mutable/ArrayDeque.scala @@ -529,7 +529,8 @@ object ArrayDeque extends StrictOptimizedSeqFactory[ArrayDeque] { val s = coll.knownSize if (s >= 0) { val array = alloc(s) - IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + val actual = IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + if (actual != s) throw new IllegalStateException(s"Copied $actual of $s") new ArrayDeque[B](array, start = 0, end = s) } else new ArrayDeque[B]() ++= coll } diff --git a/test/junit/scala/collection/mutable/ArrayBufferTest.scala b/test/junit/scala/collection/mutable/ArrayBufferTest.scala index 082fa6d0f807..933ea7fae5ce 100644 --- a/test/junit/scala/collection/mutable/ArrayBufferTest.scala +++ b/test/junit/scala/collection/mutable/ArrayBufferTest.scala @@ -7,20 +7,17 @@ import java.lang.reflect.InvocationTargetException import scala.annotation.nowarn import scala.tools.testkit.AssertUtil.{assertSameElements, assertThrows, fail} import scala.tools.testkit.ReflectUtil.{getMethodAccessible, _} +import scala.util.chaining._ class ArrayBufferTest { /* Test for scala/bug#9043 */ @Test - def testInsertAll(): Unit = { - val traver = ArrayBuffer(2, 4, 5, 7) + def testInsertAll: Unit = { + def traver = ArrayBuffer(2, 4, 5, 7) val testSeq = List(1, 3, 6, 9) - def insertAt(x: Int) = { - val clone = traver.clone() - clone.insertAll(x, testSeq) - clone - } + def insertAt(x: Int) = traver.tap(_.insertAll(x, testSeq)) // Just insert some at position 0 assertEquals(ArrayBuffer(1, 3, 6, 9, 2, 4, 5, 7), insertAt(0)) @@ -34,6 +31,9 @@ class ArrayBufferTest { // Overflow is caught assertThrows[IndexOutOfBoundsException] { insertAt(-1) } assertThrows[IndexOutOfBoundsException] { insertAt(traver.size + 10) } + + val xs = new Iterable[Int] { def iterator = Iterator(42); override def knownSize = 10 } + assertThrows[IllegalStateException] { traver.tap(_.insertAll(0, xs)) } } @Test From 6bcd34d1f00cd9643ef46e526a49c17e63b5f7ba Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 21 Oct 2022 01:47:04 -0700 Subject: [PATCH 036/261] Simplify TargetTest --- .../scala/tools/nsc/settings/TargetTest.scala | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/test/junit/scala/tools/nsc/settings/TargetTest.scala b/test/junit/scala/tools/nsc/settings/TargetTest.scala index 87eb32db9a65..7d5523430245 100644 --- a/test/junit/scala/tools/nsc/settings/TargetTest.scala +++ b/test/junit/scala/tools/nsc/settings/TargetTest.scala @@ -18,6 +18,7 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import scala.collection.mutable.ListBuffer +import scala.tools.nsc.settings.StandardScalaSettings._ import scala.util.Properties.isJavaAtLeast import scala.util.Try @@ -53,32 +54,14 @@ class TargetTest { checkFail("-target:1.9") // it's not Java 1.9, you reprobates! checkFail("-target:jvm-1.9") - check("-target:jvm-10", "10") - check("-target:10", "10") - - check("-target:jvm-11", "11") - check("-target:11", "11") - - check("-target:jvm-12", "12") - check("-target:12", "12") - - // (scene missing) - - check("-target:jvm-16", "16") - check("-target:16", "16") - - check("-target:jvm-17", "17") - check("-target:17", "17") - - check("-target:jvm-18", "18") - check("-target:18", "18") - - check("-target:jvm-19", "19") - check("-target:19", "19") - + (MinTargetVersion to MaxTargetVersion).map(_.toString).foreach { v => + check(s"-target:jvm-$v", v) + check(s"-target:$v", v) + } + checkFail(s"-target:jvm-${MaxTargetVersion+1}") + checkFail(s"-target:${MaxTargetVersion+1}") checkFail("-target:jvm-6") // no longer checkFail("-target:jvm-7") // no longer - checkFail("-target:jvm-20") // not yet... checkFail("-target:jvm-3000") // not in our lifetime checkFail("-target:msil") // really? } From 3e7fb4d493e5a5089e4fb838c5cb0ec305a73667 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 24 Oct 2022 05:58:49 -0700 Subject: [PATCH 037/261] Use narrowest type for pt of override if Xsrc:3 --- .../scala/tools/nsc/typechecker/Namers.scala | 38 ++++++++++++++----- test/files/pos/t12671.scala | 30 +++++++++++++++ 2 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 test/files/pos/t12671.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 89b110ab2ece..00081dda082c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1368,10 +1368,11 @@ trait Namers extends MethodSynthesis { deskolemizedPolySig(vparamSymss, resTpGiven) // Must be lazy about the schema to avoid cycles in neg/t5093.scala - val overridden = + def computeOverridden(immediate: Boolean) = if (!canOverride) NoSymbol - else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs _) + else safeNextOverriddenSymbolLazySchema(meth, methodSigApproxUnknownArgs _, immediate) + val overridden = computeOverridden(immediate = false) /* * If `meth` doesn't have an explicit return type, extract the return type from the method * overridden by `meth` (if there's an unique one). This type is later used as the expected @@ -1454,10 +1455,15 @@ trait Namers extends MethodSynthesis { // Add a () parameter section if this overrides some method with () parameters val vparamSymssOrEmptyParamsFromOverride = { - // must check `.info.isInstanceOf[MethodType]`, not `.isMethod`! - // Note that matching MethodType of NullaryMethodType must be nilary not nelary. + // must check `.info.isInstanceOf[MethodType]`, not `.isMethod`, to exclude NullaryMethodType. + // Note that the matching MethodType of a NullaryMethodType must be nilary not nelary. def overriddenNilary(sym: Symbol) = sym.info.isInstanceOf[MethodType] - if (overridden != NoSymbol && vparamSymss.isEmpty && overridden.alternatives.exists(overriddenNilary)) { + // always check the first override for paren purposes + def overridesNilary: Boolean = { + val toCheck = if (currentRun.isScala3) computeOverridden(immediate = true) else overridden + toCheck != NoSymbol && toCheck.alternatives.exists(overriddenNilary) + } + if (vparamSymss.isEmpty && overridesNilary) { def exempt() = meth.overrides.exists(sym => sym.isJavaDefined || isUniversalMember(sym)) val msg = "method without a parameter list overrides a method with a single empty one" def error(): Unit = if (!exempt()) { @@ -1763,20 +1769,34 @@ trait Namers extends MethodSynthesis { // Pretend we're an erroneous symbol, for now, so that we match while finding the overridden symbol, // but are not considered during implicit search. - private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType): Symbol = { + // `immediate` for immediate override only, not narrowest override + private def safeNextOverriddenSymbol(sym: Symbol, schema: Type = ErrorType, immediate: Boolean = false): Symbol = { val savedInfo = sym.rawInfo val savedFlags = sym.rawflags try { sym setInfo schema - sym.nextOverriddenSymbol + // pick the overridden symbol with narrowest type; dotty uses intersection + if (!immediate && currentRun.isScala3) { + def typeOf(s: Symbol): Type = { + val t = if (s.isMethod) s.asMethod.returnType else s.tpe + t.asSeenFrom(sym.owner.thisType, s.owner) + } + sym.allOverriddenSymbols match { + case Nil => NoSymbol + case overridden :: candidates => + candidates.foldLeft(overridden)((acc, o) => if (typeOf(o) <:< typeOf(acc)) o else acc) + } + } + else + sym.nextOverriddenSymbol } finally { sym setInfo savedInfo // setInfo resets the LOCKED flag, so restore saved flags as well sym.rawflags = savedFlags } } - private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type): Symbol = - safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() }) + private def safeNextOverriddenSymbolLazySchema(sym: Symbol, schema: () => Type, immediate: Boolean): Symbol = + safeNextOverriddenSymbol(sym, new LazyType { override def complete(sym: Symbol): Unit = sym setInfo schema() }, immediate) //@M! an abstract type definition (abstract type member/type parameter) diff --git a/test/files/pos/t12671.scala b/test/files/pos/t12671.scala new file mode 100644 index 000000000000..9db7ec760751 --- /dev/null +++ b/test/files/pos/t12671.scala @@ -0,0 +1,30 @@ + +// scalac: -Xsource:3 + +import scala.collection.{mutable, IterableOnce} +import scala.collection.immutable.{AbstractSet, Set, SetOps} + +final case class Foo[-T](components: IndexedSeq[Int]) + +sealed trait FooTrie[T] + extends AbstractSet[Foo[T]] + with SetOps[Foo[T], Set, FooTrie[T]] { + + override def fromSpecific( + coll: IterableOnce[Foo[T]] + ): FooTrie[T] = { + coll.iterator.foldLeft(empty)(_ incl _) // error here + } + + override def newSpecificBuilder + : mutable.Builder[Foo[T], FooTrie[T]] = ??? + + override def incl(elem: Foo[T]): FooTrie[T] = ??? + + override def empty = FooTrie.empty[T] + //override def empty: FooTrie[T] = FooTrie.empty[T] +} + +object FooTrie { + def empty[T]: FooTrie[T] = ??? +} From a30168dfb3bb450d377a6cc5df4d252b11bcdbee Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Wed, 26 Oct 2022 00:50:23 +0200 Subject: [PATCH 038/261] make VectorBuilder reusable again * `leftAlignPrefix()` now keeps the length of the outermost array at (LAST)WIDTH. This is necessary because after a call to `result()`, the VB may be used further. * Allow arbitrarily misleading/wrong arguments in `alignTo()` Should not fail now, even if the hint given is completely nonsensical, `leftAlignPrefix()` now compensates for that. Negative alignments are allowed, e.g. if you plan to drop elements of the vector to be added. * Add a bunch of tests to cover previously uncovered branches. --- .../scala/collection/immutable/Vector.scala | 89 ++++++++----- .../collection/immutable/VectorTest.scala | 123 +++++++++++++++++- 2 files changed, 176 insertions(+), 36 deletions(-) diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 7dd3334e7d9e..b6eca2ffd922 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -1593,32 +1593,42 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { * Removes `offset` leading `null`s in the prefix. * This is needed after calling `alignTo` and subsequent additions, * directly before the result is used for creating a new Vector. + * Note that the outermost array keeps its length to keep the + * Builder re-usable. * * example: * a2 = Array(null, ..., null, Array(null, .., null, 0, 1, .., x), Array(x+1, .., x+32), ...) * becomes - * a2 = Array(Array(0, 1, .., x), Array(x+1, .., x+32), ...) + * a2 = Array(Array(0, 1, .., x), Array(x+1, .., x+32), ..., ?, ..., ?) */ private[this] def leftAlignPrefix(): Unit = { + @inline def shrinkOffsetIfToLarge(width: Int): Unit = { + val newOffset = offset % width + lenRest -= offset - newOffset + offset = newOffset + } var a: Array[AnyRef] = null // the array we modify var aParent: Array[AnyRef] = null // a's parent, so aParent(0) == a if (depth >= 6) { a = a6.asInstanceOf[Array[AnyRef]] val i = offset >>> BITS5 - if (i > 0) { - a = copyOfRange(a, i, LASTWIDTH) - a6 = a.asInstanceOf[Arr6] - } + if (i > 0) System.arraycopy(a, i, a, 0, LASTWIDTH - i) + shrinkOffsetIfToLarge(WIDTH5) + if ((lenRest >>> BITS5) == 0) depth = 5 aParent = a a = a(0).asInstanceOf[Array[AnyRef]] } if (depth >= 5) { if (a == null) a = a5.asInstanceOf[Array[AnyRef]] val i = (offset >>> BITS4) & MASK - if (i > 0) { - a = copyOfRange(a, i, WIDTH) - if (depth == 5) a5 = a.asInstanceOf[Arr5] - else aParent(0) = a + if (depth == 5) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a5 = a.asInstanceOf[Arr5] + shrinkOffsetIfToLarge(WIDTH4) + if ((lenRest >>> BITS4) == 0) depth = 4 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a } aParent = a a = a(0).asInstanceOf[Array[AnyRef]] @@ -1626,10 +1636,14 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { if (depth >= 4) { if (a == null) a = a4.asInstanceOf[Array[AnyRef]] val i = (offset >>> BITS3) & MASK - if (i > 0) { - a = copyOfRange(a, i, WIDTH) - if (depth == 4) a4 = a.asInstanceOf[Arr4] - else aParent(0) = a + if (depth == 4) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a4 = a.asInstanceOf[Arr4] + shrinkOffsetIfToLarge(WIDTH3) + if ((lenRest >>> BITS3) == 0) depth = 3 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a } aParent = a a = a(0).asInstanceOf[Array[AnyRef]] @@ -1637,10 +1651,14 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { if (depth >= 3) { if (a == null) a = a3.asInstanceOf[Array[AnyRef]] val i = (offset >>> BITS2) & MASK - if (i > 0) { - a = copyOfRange(a, i, WIDTH) - if (depth == 3) a3 = a.asInstanceOf[Arr3] - else aParent(0) = a + if (depth == 3) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a3 = a.asInstanceOf[Arr3] + shrinkOffsetIfToLarge(WIDTH2) + if ((lenRest >>> BITS2) == 0) depth = 2 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a } aParent = a a = a(0).asInstanceOf[Array[AnyRef]] @@ -1648,10 +1666,14 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { if (depth >= 2) { if (a == null) a = a2.asInstanceOf[Array[AnyRef]] val i = (offset >>> BITS) & MASK - if (i > 0) { - a = copyOfRange(a, i, WIDTH) - if (depth == 2) a2 = a.asInstanceOf[Arr2] - else aParent(0) = a + if (depth == 2) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a2 = a.asInstanceOf[Arr2] + shrinkOffsetIfToLarge(WIDTH) + if ((lenRest >>> BITS) == 0) depth = 1 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a } aParent = a a = a(0).asInstanceOf[Array[AnyRef]] @@ -1659,10 +1681,14 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { if (depth >= 1) { if (a == null) a = a1.asInstanceOf[Array[AnyRef]] val i = offset & MASK - if (i > 0) { - a = copyOfRange(a, i, WIDTH) - if (depth == 1) a1 = a.asInstanceOf[Arr1] - else aParent(0) = a + if (depth == 1) { + if (i > 0) System.arraycopy(a, i, a, 0, WIDTH - i) + a1 = a.asInstanceOf[Arr1] + len1 -= offset + offset = 0 + } else { + if (i > 0) a = copyOfRange(a, i, WIDTH) + aParent(0) = a } } prefixIsRightAligned = false @@ -1761,15 +1787,13 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { slice.foreach(e => addArrN(e.asInstanceOf[Array[AnyRef]], 5)) return } - val copy1 = mmin((BITS * 6 + 1 - lenRest) >>> BITS5, sl) - val copy2 = sl - copy1 + val copy1 = sl + // there is no copy2 because there can't be another a6 to copy to val destPos = lenRest >>> BITS5 + if (destPos + copy1 > LASTWIDTH) + throw new IllegalArgumentException("exceeding 2^31 elements") System.arraycopy(slice, 0, a6, destPos, copy1) advanceN(WIDTH5 * copy1) - if (copy2 > 0) { - System.arraycopy(slice, copy1, a6, 0, copy2) - advanceN(WIDTH5 * copy2) - } } } @@ -1867,8 +1891,7 @@ final class VectorBuilder[A] extends ReusableBuilder[A, Vector[A]] { if(realLen == 0) Vector.empty else if(len < 0) throw new IndexOutOfBoundsException(s"Vector cannot have negative size $len") else if(len <= WIDTH) { - if(realLen == WIDTH) new Vector1(a1) - else new Vector1(copyOf(a1, realLen)) + new Vector1(copyIfDifferentSize(a1, realLen)) } else if(len <= WIDTH2) { val i1 = (len-1) & MASK val i2 = (len-1) >>> BITS diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index a9b77d4b9745..70257ccf3bd9 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -6,6 +6,7 @@ import org.junit.runners.JUnit4 import org.junit.Test import scala.annotation.unused +import scala.collection.immutable.VectorInline.{WIDTH3, WIDTH4, WIDTH5} import scala.collection.mutable.{ListBuffer, StringBuilder} import scala.tools.testkit.AssertUtil.intercept @@ -110,21 +111,43 @@ class VectorTest { def testBuilderAlignTo2(): Unit = { val Large = 1 << 20 for ( - size <- Seq(0, 1, 31, 1 << 5, 1 << 10, 1 << 15, 1 << 20, 1 << 25, 1 << 30, (1 << 31) - (1 << 26) - 1000); + size <- Seq(0, 1, 31, 1 << 5, 1 << 10, 1 << 15, 1 << 20, 9 << 20, 1 << 25, 9 << 25, 50 << 25, 1 << 30, (1 << 31) - (1 << 26) - 1000); i <- Seq(0, 1, 5, 123) ) { // println((i, size)) val v = if (size < Large) Vector.tabulate(size)(_.toString) else Vector.fillSparse(size)("v") val prefix = Vector.fill(i)("prefix") - val res = new VectorBuilder[AnyRef] + val vb = new VectorBuilder[AnyRef] .alignTo(i, v) .addAll(prefix) .addAll(v) - .result() + val res = vb.result() val vDesc = if (v.headOption.contains("v")) s"Vector(\"v\")*$size" else s"Vector(0,..,${size-1})" assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).size", size + i, res.size) assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).take($i)", prefix, res.take(i)) assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc).drop($i)", v.take(Large), res.drop(i).take(Large)) + + if (size == 9 << 20) { + val v4 = Vector.fillSparse(WIDTH3 * 2)("v4") + val suffix = (v4 ++ v).take(size) + val res2 = vb.addAll(suffix).result() + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).size", 2 * size + i, res2.size) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).take($i)", prefix, res2.take(i)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop($i).take($size)", v.take(Large), res2.drop(i).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + size}).take($size)", suffix.take(Large), res2.drop(i + size).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + 2 * size})", suffix.drop(size).take(Large), res2.drop(i + 2 * size).take(Large)) + } else if (size == 9 << 25) { + val v4 = Vector.fillSparse(WIDTH4 * 2)("v4") + val suffix = (v4 ++ v).take(size) + val res2 = vb.addAll(suffix).result() + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).size", 2 * size + i, res2.size) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).take($i)", prefix, res2.take(i)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop($i).take($size)", v.take(Large), res2.drop(i).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + size}).take($size)", suffix.take(Large), res2.drop(i + size).take(Large)) + assertEquals(s"(Vector(\"prefix\")*$i ++ $vDesc ++ v4 ++ $vDesc).drop(${i + 2 * size})", suffix.drop(size).take(Large), res2.drop(i + 2 * size).take(Large)) + } else if (size == 50 << 25) { + assertThrows(classOf[IllegalArgumentException], () => vb.addAll(v)) + } } } @@ -139,6 +162,100 @@ class VectorTest { assertEquals(copy.take(500), v.take(500)) } + @Test + def testWierdAlignments1(): Unit = { + val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] + for (i <- Seq(0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000)) { + val res = new VectorBuilder[AnyRef] + .alignTo(i, v3) // pretend to add i elements before v3, but then decide not to... this is slow, but should not fail + .addAll(v3) + .result() + .asInstanceOf[Vector3[AnyRef]] + assertEquals(s"vectors equal", v3, res) + } + } + + @Test + def testWierdAlignments2(): Unit = { + val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] + val v2 = Vector.tabulate(765)(i => (i - 123).toString).drop(123).asInstanceOf[Vector2[AnyRef]] + for (i <- Seq(-1234, -42, -1, 0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000)) { + val res = new VectorBuilder[AnyRef] + .alignTo(i, v3) // pretend to add v3 ... + .addOne("a") + .addAll(v2) // ... but then add completely unrelated v2 instead! + .result() + assertEquals(s"vectors equal", "a" +: v2, res) + } + } + + @Test + def testWierdAlignments3(): Unit = for (n <- allSizes; m <- verySmallSizes) { + val vPretend = + if (smallSizes.contains(n)) + Vector.tabulate(n + 1337)(i => (i - 1337).toString).drop(1337) + else + Vector.fillSparse(n + 1337)("v").drop(1337) + val vReal = Vector.tabulate(m + 42)(i => (i - 42).toString).drop(42) + for (i <- Seq(-1234, -42, -1, 0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000, 1234567)) { + val vb = new VectorBuilder[AnyRef] + .alignTo(i, vPretend) // pretend to add vPretend ... + .addAll(vReal) // ... but then add completely unrelated vReal instead! + val res1 = vb.result() + assertEquals(s"vectors not equal, n=$n, m=$m, i=$i", vReal, res1) + val res2 = vb + .addOne("a") + .addAll(vReal) // reuse the builder + .result() + assertEquals(s"vectors not equal, n=$n, m=$m, i=$i", (vReal :+ "a") ++ vReal, res2) + } + } + + @Test + def testWierdAlignments4(): Unit = { + var lengths = Set[Int]() + for ( + n <- allSizes.init :+ (allSizes.last - WIDTH5 - WIDTH3 + 41); // we need WIDTH5 for prefix, add 1+WIDTH3 and get 42 in suffix free + m <- List(WIDTH4, WIDTH5, Int.MaxValue - WIDTH5) + ) { + val vPretend = Vector.fillSparse(42)("v0") ++ Vector.fillSparse(m - 42)("v1") + val vReal = vPretend.take(n) + // if (n==1073741824 && m==2046820352) + // println(s"n=$n, m=$m") + val vb = new VectorBuilder[AnyRef] + .alignTo(0, vPretend) // pretend to add vPretend ... + .addAll(vReal) // ... but then add only a subsequence! + val res1 = vb.result() + assertEquals(s"vectors not equal, n=$n, m=$m", vReal.length, res1.length) + assertEquals(s"vectors not equal, n=$n, m=$m", vReal.take(WIDTH3), res1.take(WIDTH3)) + assertEquals(s"vectors not equal, n=$n, m=$m", vReal.takeRight(WIDTH3), res1.takeRight(WIDTH3)) + val res2 = vb + .addOne("a") + .addAll(vReal.take(WIDTH3)) // whole vector may take too long + .result() + val expected = (vReal :+ "a") ++ (vReal.take(WIDTH3)) + assertEquals(s"vectors not equal, n=$n, m=$m", expected.length, res2.length) + assertEquals(s"vectors not equal, n=$n, m=$m", expected.take(WIDTH3), res2.take(WIDTH3)) + assertEquals(s"vectors not equal, n=$n, m=$m", expected.takeRight(WIDTH3), res2.takeRight(WIDTH3)) + lengths += res2.length + } + assertEquals(15, lengths.size) + assertEquals(Int.MaxValue - WIDTH5 + 42, lengths.max) + } + + @Test + def testNegativeAlignment(): Unit = for (size <- allSizes; i <- allSizes) { + val v = Vector.fillSparse(math.min(63 * WIDTH5, size))("v") + val expected = v.drop(i) + val vb = new VectorBuilder[AnyRef] + .alignTo(-i, v) + .addAll(expected) + val res = vb.result() + assertEquals("lengths not equal", expected.length, res.length) + assertEquals(s"vectors not equal", expected.take(WIDTH3), res.take(WIDTH3)) + assertEquals(s"vectors not equal", expected.takeRight(WIDTH3), res.takeRight(WIDTH3)) + } + @Test def factoryReuse(): Unit = { assertSame(Vector.empty, Vector.empty) From 30b3d58745fa55d95b2beaa9314a3fdf2cd18ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Sumis=C5=82awski?= Date: Mon, 17 Oct 2022 19:36:49 +0200 Subject: [PATCH 039/261] Fix -Yprofile-enabled on java 9+ --- .../tools/nsc/profile/ExtendedThreadMxBean.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java b/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java index 1d5cf4bc3e4e..f580e16ba496 100644 --- a/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java +++ b/src/compiler/scala/tools/nsc/profile/ExtendedThreadMxBean.java @@ -260,13 +260,14 @@ public SunThreadMxBean(ThreadMXBean underlying) { super(underlying); this.real = underlying; try { - getThreadUserTimeMethod = real.getClass().getMethod("getThreadUserTime", long[].class); - isThreadAllocatedMemoryEnabledMethod = real.getClass().getMethod("isThreadAllocatedMemoryEnabled"); - setThreadAllocatedMemoryEnabledMethod = real.getClass().getMethod("setThreadAllocatedMemoryEnabled", Boolean.TYPE); - getThreadAllocatedBytesMethod1 = real.getClass().getMethod("getThreadAllocatedBytes", Long.TYPE); - getThreadAllocatedBytesMethod2 = real.getClass().getMethod("getThreadAllocatedBytes", long[].class); - isThreadAllocatedMemorySupportedMethod = real.getClass().getMethod("isThreadAllocatedMemorySupported"); - getThreadCpuTimeMethod = real.getClass().getMethod("getThreadCpuTime", long[].class); + Class cls = Class.forName("com.sun.management.ThreadMXBean"); + getThreadUserTimeMethod = cls.getMethod("getThreadUserTime", long[].class); + isThreadAllocatedMemoryEnabledMethod = cls.getMethod("isThreadAllocatedMemoryEnabled"); + setThreadAllocatedMemoryEnabledMethod = cls.getMethod("setThreadAllocatedMemoryEnabled", Boolean.TYPE); + getThreadAllocatedBytesMethod1 = cls.getMethod("getThreadAllocatedBytes", Long.TYPE); + getThreadAllocatedBytesMethod2 = cls.getMethod("getThreadAllocatedBytes", long[].class); + isThreadAllocatedMemorySupportedMethod = cls.getMethod("isThreadAllocatedMemorySupported"); + getThreadCpuTimeMethod = cls.getMethod("getThreadCpuTime", long[].class); getThreadUserTimeMethod.setAccessible(true); isThreadAllocatedMemoryEnabledMethod.setAccessible(true); From ed358f02b4e491cd498a4e417df36a4203a0de44 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 23 Sep 2022 19:17:58 -0700 Subject: [PATCH 040/261] Update optimizer class path when release is set --- src/compiler/scala/tools/nsc/Global.scala | 19 +++++++-- .../tools/nsc/interpreter/ReplGlobal.scala | 5 ++- test/files/neg/t3420b.check | 4 ++ test/files/neg/t3420b.scala | 9 ++++ test/files/pos/t3420b.scala | 8 ++++ test/files/run/repl-release.check | 42 +++++++++++++++++++ test/files/run/repl-release.scala | 33 +++++++++++++++ 7 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 test/files/neg/t3420b.check create mode 100644 test/files/neg/t3420b.scala create mode 100644 test/files/pos/t3420b.scala create mode 100644 test/files/run/repl-release.check create mode 100644 test/files/run/repl-release.scala diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 926496d913fb..44f612b172a1 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -135,9 +135,22 @@ class Global(var currentSettings: Settings, reporter0: Reporter) type ThisPlatform = JavaPlatform { val global: Global.this.type } lazy val platform: ThisPlatform = new GlobalPlatform - /* A hook for the REPL to add a classpath entry containing products of previous runs to inliner's bytecode repository*/ - // Fixes scala/bug#8779 - def optimizerClassPath(base: ClassPath): ClassPath = base + + /* Create a class path for the backend, based on the given class path. + * Used to make classes available to the inliner's bytecode repository. + * + * In particular, if ct.sym is used for compilation, replace it with jrt. + * + * See ReplGlobal, which appends a classpath entry containing products of previous runs. (Fixes scala/bug#8779.) + */ + def optimizerClassPath(base: ClassPath): ClassPath = + base match { + case AggregateClassPath(entries) if entries.head.isInstanceOf[CtSymClassPath] => + JrtClassPath(release = None, closeableRegistry) + .map(jrt => AggregateClassPath(entries.drop(1).prepended(jrt))) + .getOrElse(base) + case _ => base + } def classPath: ClassPath = platform.classPath diff --git a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala index 7111a31c88b6..4c3b8ef1e97f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -98,12 +98,13 @@ trait ReplGlobal extends Global { } override def optimizerClassPath(base: ClassPath): ClassPath = { + val base1 = super.optimizerClassPath(base) settings.outputDirs.getSingleOutput match { - case None => base + case None => base1 case Some(out) => // Make bytecode of previous lines available to the inliner val replOutClasspath = ClassPathFactory.newClassPath(settings.outputDirs.getSingleOutput.get, settings, closeableRegistry) - AggregateClassPath.createAggregate(platform.classPath, replOutClasspath) + AggregateClassPath.createAggregate(base1, replOutClasspath) } } diff --git a/test/files/neg/t3420b.check b/test/files/neg/t3420b.check new file mode 100644 index 000000000000..3770c326dede --- /dev/null +++ b/test/files/neg/t3420b.check @@ -0,0 +1,4 @@ +t3420b.scala:8: error: value translateEscapes is not a member of String + def f(s: String) = s.translateEscapes + ^ +1 error diff --git a/test/files/neg/t3420b.scala b/test/files/neg/t3420b.scala new file mode 100644 index 000000000000..33d907594145 --- /dev/null +++ b/test/files/neg/t3420b.scala @@ -0,0 +1,9 @@ + +// scalac: --release 8 -opt:inline:** -Wopt -Werror +// +class C { + val cv = Map[Int, Int](1 -> 2) + lazy val cl = Map[Int, Int](1 -> 2) + def cd = Map[Int, Int](1 -> 2) + def f(s: String) = s.translateEscapes +} diff --git a/test/files/pos/t3420b.scala b/test/files/pos/t3420b.scala new file mode 100644 index 000000000000..6b5f61019633 --- /dev/null +++ b/test/files/pos/t3420b.scala @@ -0,0 +1,8 @@ + +// scalac: --release 8 -opt:inline:** -Wopt -Werror +// +class C { + val cv = Map[Int, Int](1 -> 2) + lazy val cl = Map[Int, Int](1 -> 2) + def cd = Map[Int, Int](1 -> 2) +} diff --git a/test/files/run/repl-release.check b/test/files/run/repl-release.check new file mode 100644 index 000000000000..4b245b2dd5da --- /dev/null +++ b/test/files/run/repl-release.check @@ -0,0 +1,42 @@ + +scala> def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName +def callerOfCaller: String + +scala> @noinline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "g", h) + +scala> @inline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "h", h) + +scala> :quit + +scala> def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName +def callerOfCaller: String + +scala> @noinline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "g", h) + +scala> @inline def g = callerOfCaller +def g: String + +scala> @noinline def h = g +def h: String + +scala> assert(h == "h", h) + +scala> :quit diff --git a/test/files/run/repl-release.scala b/test/files/run/repl-release.scala new file mode 100644 index 000000000000..82ab1807678e --- /dev/null +++ b/test/files/run/repl-release.scala @@ -0,0 +1,33 @@ +import scala.tools.partest.ReplTest +import scala.tools.nsc._ +import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter.shell.ReplReporterImpl + +// cf run/repl-inline.scala +object Test extends ReplTest { + + var count = 0 + + override def transformSettings(s: Settings) = { + s.processArguments("-release:8" :: "-opt:inline:**" :: "-Wopt" :: Nil, processAll = true) + s.Yreplclassbased.value = count > 0 + count += 1 + s + } + + override def code = + """ +def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName +@noinline def g = callerOfCaller +@noinline def h = g +assert(h == "g", h) +@inline def g = callerOfCaller +@noinline def h = g +assert(h == "h", h) + """ + + override def show() = { + super.show() + super.show() + } +} From 0b92c3c65d7cb15d53e833ced5814dd669b961df Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 27 Oct 2022 11:50:39 +0200 Subject: [PATCH 041/261] Fix accessibility case for static java members Given ``` public class A { protected static class AI { } } public class B extends A { public static class BI extends AI { } } ``` The owner of `AI` is the module class `A$`, the owner of `BI` is the module class `B$`. When checking if the protected `AI` can be accessed in `B$`, we need to navigate from `B$` to `B`. The inheritance chain is not reflected in the module classes. --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 4 +++- test/files/pos/t12673/A.java | 4 ++++ test/files/pos/t12673/B.java | 6 ++++++ test/files/pos/t12673/Test.scala | 5 +++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t12673/A.java create mode 100644 test/files/pos/t12673/B.java create mode 100644 test/files/pos/t12673/Test.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 381b468aaa8b..afca32067c2f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -896,7 +896,9 @@ trait Contexts { self: Analyzer => private def isSubClassOrCompanion(sub: Symbol, base: Symbol) = sub.isNonBottomSubClass(base) || (sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)) || - (base.isJavaDefined && base.isModuleClass && sub.isNonBottomSubClass(base.linkedClassOfClass)) + (base.isJavaDefined && base.isModuleClass && ( + sub.isNonBottomSubClass(base.linkedClassOfClass) || + sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base.linkedClassOfClass))) /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. diff --git a/test/files/pos/t12673/A.java b/test/files/pos/t12673/A.java new file mode 100644 index 000000000000..faaf0d26761f --- /dev/null +++ b/test/files/pos/t12673/A.java @@ -0,0 +1,4 @@ +package compiletest.a; +public class A { + protected static class InnerParent { } +} diff --git a/test/files/pos/t12673/B.java b/test/files/pos/t12673/B.java new file mode 100644 index 000000000000..d94349074731 --- /dev/null +++ b/test/files/pos/t12673/B.java @@ -0,0 +1,6 @@ +package compiletest.b; +import compiletest.a.A; + +public class B extends A { + public static class InnerChild extends InnerParent { } +} diff --git a/test/files/pos/t12673/Test.scala b/test/files/pos/t12673/Test.scala new file mode 100644 index 000000000000..1f867027731b --- /dev/null +++ b/test/files/pos/t12673/Test.scala @@ -0,0 +1,5 @@ +package client + +object Client { + new compiletest.b.B.InnerChild +} From 05e94065cbc825093eb5d99d4aaf4deac2ecd3c9 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 23 Sep 2022 23:02:23 -0700 Subject: [PATCH 042/261] Partest realeasy mode --- project/PartestUtil.scala | 2 +- .../scala/tools/partest/nest/AbstractRunner.scala | 1 + src/partest/scala/tools/partest/nest/Runner.scala | 15 +++++++++++---- .../scala/tools/partest/nest/RunnerSpec.scala | 5 +++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/project/PartestUtil.scala b/project/PartestUtil.scala index dfe8819a7a6f..a70402ae7cc9 100644 --- a/project/PartestUtil.scala +++ b/project/PartestUtil.scala @@ -36,7 +36,7 @@ object PartestUtil { val knownUnaryOptions = List( "--pos", "--neg", "--run", "--jvm", "--res", "--ant", "--scalap", "--specialized", "--instrumented", "--presentation", "--failed", "--update-check", "--no-exec", - "--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--version", "--help") + "--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--realeasy", "--version", "--help") val srcPathOption = "--srcpath" val compilerPathOption = "--compilerpath" val grepOption = "--grep" diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index cb1a2db35c19..1035407fb779 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -33,6 +33,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour val debug: Boolean = config.optDebug || propOrFalse("partest.debug") val verbose: Boolean = config.optVerbose val terse: Boolean = config.optTerse + val realeasy: Boolean = config.optDev protected val printSummary = true protected val partestCmd = "test/partest" diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 9ec1153fd011..d67c69de4ea2 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -33,6 +33,7 @@ import TestState.{Crash, Fail, Pass, Skip, Updated} import FileManager.{compareContents, joinPaths, withTempFile} import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader import scala.util.{Failure, Success, Try, Using} +import scala.util.Properties.isJavaAtLeast import scala.util.chaining._ import scala.util.control.ControlThrowable @@ -453,11 +454,17 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } // all sources in a round may contribute flags via // scalac: -flags + // under --realeasy, if a javaVersion isn't specified, require the minimum viable using -release 8 + // to avoid accidentally committing a test that requires a later JVM. def flagsForCompilation(sources: List[File]): List[String] = { - val perFile = toolArgsFor(sources)("scalac") - if (parentFile.getParentFile.getName == "macro-annot") "-Ymacro-annotations" :: perFile - else perFile - } + var perFile = toolArgsFor(sources)("scalac") + if (parentFile.getParentFile.getName == "macro-annot") + perFile ::= "-Ymacro-annotations" + if (realeasy && isJavaAtLeast(9) && !perFile.exists(releaseFlag.matches) && toolArgsFor(sources)("javaVersion", split = false).isEmpty) + perFile ::= "-release:8" + perFile + } + private val releaseFlag = raw"--?release(?::\d+)?".r // inspect sources for tool args def toolArgs(tool: String, split: Boolean = true): List[String] = diff --git a/src/partest/scala/tools/partest/nest/RunnerSpec.scala b/src/partest/scala/tools/partest/nest/RunnerSpec.scala index 80c1bae94c02..e6a765f17e28 100644 --- a/src/partest/scala/tools/partest/nest/RunnerSpec.scala +++ b/src/partest/scala/tools/partest/nest/RunnerSpec.scala @@ -56,8 +56,9 @@ trait RunnerSpec extends Spec with Meta.StdOpts with Interpolation { val optDebug = "debug" / "enable debugging output, preserve generated files" --? heading("Other options:") - val optVersion = "version" / "show Scala version and exit" --? - val optHelp = "help" / "show this page and exit" --? + val optDev = "realeasy" / "real easy way to test --release 8 and check uncommitted checks" --? + val optVersion = "version" / "show Scala version and exit" --? + val optHelp = "help" / "show this page and exit" --? } From 7de1c896636dea4b918c131b1362560dd1ea6ddd Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 24 Sep 2022 00:52:00 -0700 Subject: [PATCH 043/261] Partest realeasy warns about uncommitted check files --- .../tools/partest/nest/AbstractRunner.scala | 29 +++++++++++++++---- .../scala/tools/partest/nest/Runner.scala | 14 ++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index 1035407fb779..c3e3ec76465c 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -18,6 +18,7 @@ import utils.Properties._ import scala.tools.nsc.Properties.{propOrFalse, setProp, versionMsg} import scala.collection.mutable import scala.reflect.internal.util.Collections.distinctBy +import scala.sys.process.Process import scala.util.{Try, Success, Failure} import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -36,7 +37,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour val realeasy: Boolean = config.optDev protected val printSummary = true - protected val partestCmd = "test/partest" + protected val partestCmd = "partest" private[this] var totalTests = 0 private[this] val passedTests = mutable.ListBuffer[TestState]() @@ -66,6 +67,12 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour private[this] val realSysErr = System.err + val gitRunner = List("/usr/local/bin/git", "/usr/bin/git").map(f => new java.io.File(f)).find(_.canRead) + def runGit[R](cmd: String)(f: LazyList[String] => R): Option[R] = + Try { + gitRunner.map(git => f(Process(s"$git $cmd").lazyLines_!)) + }.toOption.flatten + def statusLine(state: TestState, durationMs: Long) = { import state._ import TestState._ @@ -164,16 +171,28 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour } } - val bslash = "\\" - def files_s = failed0.map(_.testFile).mkString(s" ${bslash}\n ") - echo("# Failed test paths (this command will update checkfiles)") - echo(s"$partestCmd --update-check ${bslash}\n $files_s\n") + if (failed0.size == 1) { + echo("# A test failed. To update the check file:") + echo(s"$partestCmd --update-check ${failed0.head.testIdent}") + } + else { + val bslash = "\\" + def files_s = failed0.map(_.testFile).mkString(s" ${bslash}\n ") + echo("# Failed test paths (this command will update checkfiles)") + echo(s"$partestCmd --update-check ${bslash}\n $files_s\n") + } } if (printSummary) { echo(message) levyJudgment() } + if (realeasy) { + runGit("status --porcelain")(_.filter(_.endsWith(".check")).map(_.drop(3)).mkString("\n")).foreach { danger => + echo(bold(red("# There are uncommitted check files!"))) + echo(s"$danger\n") + } + } } } diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index d67c69de4ea2..442f95ad1d58 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -333,18 +333,10 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { compareContents(original = checked, revised = logged, originalName = checkname, revisedName = logFile.getName) } - val gitRunner = List("/usr/local/bin/git", "/usr/bin/git") map (f => new java.io.File(f)) find (_.canRead) - val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options") - // --color=always --word-diff - def gitDiff(f1: File, f2: File): Option[String] = { - try gitRunner map { git => - val cmd = s"$git diff $gitDiffOptions $f1 $f2" - val diff = Process(cmd).lazyLines_!.drop(4).map(_ + "\n").mkString - - "\n" + diff - } - catch { case t: Exception => None } + val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options") + // --color=always --word-diff + runGit(s"diff $gitDiffOptions $f1 $f2")(_.drop(4).map(_ + "\n").mkString).map("\n" + _) } /** Normalize the log output by applying test-specific filters From cd0a99f4fd89a1be3f2fc639ae3b54f5592d88cd Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 27 Oct 2022 04:04:44 -0700 Subject: [PATCH 044/261] Careful interpolating windows paths --- test/files/run/t12390.scala | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/files/run/t12390.scala b/test/files/run/t12390.scala index 73f2bb7286ad..edf7c5034d35 100644 --- a/test/files/run/t12390.scala +++ b/test/files/run/t12390.scala @@ -1,37 +1,39 @@ import scala.tools.nsc.Settings import scala.tools.nsc.interpreter._, shell._ +import scala.util.Properties.lineSeparator -import java.io.{PrintWriter, OutputStream} +import java.io.{PrintWriter, StringWriter} object Test { def main(args: Array[String]): Unit = { - //val filename: String = ".../data/generated_01mb.base64" - val filename: String = sys.props("partest.test-path") val code: String = - "import scala.io.Source\n " + - "Source.fromFile(\"" + filename + "\").getLines().mkString(\"\\n\")" + s"""|import scala.io.Source + |import scala.util.Properties.lineSeparator + |import scala.util.chaining._ + |Source.fromFile(sys.props("partest.test-path")).pipe(s => s.getLines().mkString(lineSeparator).tap(_ => s.close())) + |""".stripMargin.linesIterator.mkString(lineSeparator) val s = new Settings() s.processArguments( List( - //"-Xprint:typer", "-deprecation", "-Yrepl-class-based", - "-Yrepl-outdir", "./target" + "-Yrepl-outdir", "target" ), processAll = true) - val drain = new OutputStream { override def write(byte: Int) = () } + val drain = new StringWriter //new OutputStream { override def write(byte: Int) = () } val sinkWriter = new PrintWriter(drain) val reporter = new ReplReporterImpl(ShellConfig(s), s, sinkWriter) val repl = new IMain(s, reporter) repl.settings.usejavacp.value = true - for(i <- 1 to 65) { + for (i <- 1 to 65) { repl.interpret(code) match { case Results.Success => assert(repl.valueOfTerm(repl.mostRecentVar).get != null) // 2.12: null after 60 case other => + println(drain.toString) throw new MatchError(other) } } From 96b96aab87bd31ad9dc084d2bd91445725d62b21 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 31 Oct 2022 00:40:20 -0700 Subject: [PATCH 045/261] Eval class and enum constants --- .../scala/reflect/macros/contexts/Evals.scala | 8 +++++++- test/files/run/t12680/J_1.java | 1 + test/files/run/t12680/c_3.scala | 2 ++ test/files/run/t12680/m_2.scala | 15 +++++++++++++++ test/files/run/t12680/test_4.scala | 8 ++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t12680/J_1.java create mode 100644 test/files/run/t12680/c_3.scala create mode 100644 test/files/run/t12680/m_2.scala create mode 100644 test/files/run/t12680/test_4.scala diff --git a/src/compiler/scala/reflect/macros/contexts/Evals.scala b/src/compiler/scala/reflect/macros/contexts/Evals.scala index fbff3a2fb8fe..d781dfce5198 100644 --- a/src/compiler/scala/reflect/macros/contexts/Evals.scala +++ b/src/compiler/scala/reflect/macros/contexts/Evals.scala @@ -24,8 +24,14 @@ trait Evals { private lazy val evalImporter = ru.internal.createImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }] def eval[T](expr: Expr[T]): T = { + def specialK(x: Any) = + x match { + case _: global.TypeRef => true + case _: global.Symbol => true + case _ => false + } expr.tree match { - case global.Literal(global.Constant(value)) => + case global.Literal(global.Constant(value)) if !specialK(value) => value.asInstanceOf[T] case _ => val imported = evalImporter.importTree(expr.tree) diff --git a/test/files/run/t12680/J_1.java b/test/files/run/t12680/J_1.java new file mode 100644 index 000000000000..4431282356bc --- /dev/null +++ b/test/files/run/t12680/J_1.java @@ -0,0 +1 @@ +public enum J_1 { FOO, BAR } diff --git a/test/files/run/t12680/c_3.scala b/test/files/run/t12680/c_3.scala new file mode 100644 index 000000000000..311da69beba0 --- /dev/null +++ b/test/files/run/t12680/c_3.scala @@ -0,0 +1,2 @@ + +class C diff --git a/test/files/run/t12680/m_2.scala b/test/files/run/t12680/m_2.scala new file mode 100644 index 000000000000..1ed2a5278a0d --- /dev/null +++ b/test/files/run/t12680/m_2.scala @@ -0,0 +1,15 @@ + +import language.experimental.macros +import scala.reflect.macros._ + +object M { + def f(clazz: Class[_], j: J_1): String = macro g + + def g(c: blackbox.Context)(clazz: c.Tree, j: c.Tree): c.Tree = { + import c.universe._ + val classValue = c.eval(c.Expr[Class[_]](c.untypecheck(clazz))) + val jValue = c.eval(c.Expr[J_1](c.untypecheck(j))) + + Literal(Constant(s"$classValue, $jValue")) + } +} diff --git a/test/files/run/t12680/test_4.scala b/test/files/run/t12680/test_4.scala new file mode 100644 index 000000000000..4b1e28164c04 --- /dev/null +++ b/test/files/run/t12680/test_4.scala @@ -0,0 +1,8 @@ + +object Test extends App { + import J_1._ + import M._ + + val res = f(classOf[C], FOO) + assert(res == "class C, FOO") +} From 5478e024064e4ca6bfdb19e130fb23dc52ba850d Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 31 Oct 2022 11:55:11 +0100 Subject: [PATCH 046/261] sbt 1.7.3 (was .2) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 563a014da4aa..6a9f0388941f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.2 +sbt.version=1.7.3 From 7b8c40771b43b53d6ce680508d5f4034c5de1bee Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Mon, 31 Oct 2022 14:17:31 +0100 Subject: [PATCH 047/261] use VB.alignTo only if RHS is significantly larger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * To cempensate for alignTo's (small, but constant) overhead, it is only called when the right Vector is mor then 64 elemente longer than the left one. * replaced some `….knownSize` by `k` --- .../scala/collection/immutable/Vector.scala | 11 ++++-- ...ectorConcatAlignToWorstCaseBenchmark.scala | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index b6eca2ffd922..af593e57d029 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -206,6 +206,7 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va } protected[this] def prependedAll0[B >: A](prefix: collection.IterableOnce[B], k: Int): Vector[B] = { + // k >= 0, k = prefix.knownSize val tinyAppendLimit = 4 + vectorSliceCount if (k < tinyAppendLimit /*|| k < (this.size >>> Log2ConcatFaster)*/) { var v: Vector[B] = this @@ -217,14 +218,15 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va val it = this.iterator while (it.hasNext) v = v :+ it.next() v - } else if (prefix.knownSize >= 0 && prefix.knownSize < this.size) { - new VectorBuilder[B].alignTo(prefix.knownSize, this).addAll(prefix).addAll(this).result() + } else if (k < this.size - AlignToFaster) { + new VectorBuilder[B].alignTo(k, this).addAll(prefix).addAll(this).result() } else super.prependedAll(prefix) } protected[this] def appendedAll0[B >: A](suffix: collection.IterableOnce[B], k: Int): Vector[B] = { + // k >= 0, k = suffix.knownSize val tinyAppendLimit = 4 + vectorSliceCount - if(k > 0 && k < tinyAppendLimit) { + if (k < tinyAppendLimit) { var v: Vector[B] = this suffix match { case it: Iterable[_] => it.asInstanceOf[Iterable[B]].foreach(x => v = v.appended(x)) @@ -236,7 +238,7 @@ sealed abstract class Vector[+A] private[immutable] (private[immutable] final va val ri = this.reverseIterator while (ri.hasNext) v = v.prepended(ri.next()) v - } else if (this.size < suffix.knownSize && suffix.isInstanceOf[Vector[_]]) { + } else if (this.size < k - AlignToFaster && suffix.isInstanceOf[Vector[_]]) { val v = suffix.asInstanceOf[Vector[B]] new VectorBuilder[B].alignTo(this.size, v).addAll(this).addAll(v).result() } else new VectorBuilder[B].initFrom(this).addAll(suffix).result() @@ -2000,6 +2002,7 @@ private[immutable] object VectorInline { final val WIDTH5 = 1 << BITS5 final val LASTWIDTH = WIDTH << 1 // 1 extra bit in the last level to go up to Int.MaxValue (2^31-1) instead of 2^30: final val Log2ConcatFaster = 5 + final val AlignToFaster = 64 type Arr1 = Array[AnyRef] type Arr2 = Array[Array[AnyRef]] diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala new file mode 100644 index 000000000000..51c269f78712 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorConcatAlignToWorstCaseBenchmark.scala @@ -0,0 +1,39 @@ +package scala.collection.immutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(1) +@Threads(1) +@Warmup(iterations = 4) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class VectorConcatAlignToWorstCaseBenchmark { + @Param(Array("1", "15", "31", "33", "63", "65", "127", "255", "513", "1023", "1025", "2047")) // should not be divisible by 32 + var size: Int = _ + @Param(Array("1", "32", "64", "128", "256")) + var sizeDifference: Int = _ + + val o = new AnyRef + + var shorter: Vector[String] = _ + var longer: Vector[String] = _ + + @Setup(Level.Trial) def init(): Unit = { + shorter = Vector.fill(size)("s") + longer = Vector.fill(size + sizeDifference)("l") + } + + @Benchmark def withoutAlignTo(bh: Blackhole): Any = + bh.consume(new VectorBuilder[String]().addAll(shorter).addAll(longer).result()) + + @Benchmark def withAlignTo(bh: Blackhole): Any = + bh.consume(new VectorBuilder[String]().alignTo(shorter.length, longer).addAll(shorter).addAll(longer).result()) + + @Benchmark def concat(bh: Blackhole): Any = + bh.consume(shorter ++ longer) +} From d874f797193e2a581d7a490ce9ac55b707d6bb51 Mon Sep 17 00:00:00 2001 From: danarmak Date: Mon, 31 Oct 2022 16:39:09 +0200 Subject: [PATCH 048/261] Document: don't allow characters with unicode property Bidi_Class in source files Update documentation with changes made in scala#10017 --- spec/01-lexical-syntax.md | 5 +++++ spec/13-syntax-summary.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/spec/01-lexical-syntax.md b/spec/01-lexical-syntax.md index 921384b40e20..36f6daaa989d 100644 --- a/spec/01-lexical-syntax.md +++ b/spec/01-lexical-syntax.md @@ -8,6 +8,11 @@ chapter: 1 Scala source code consists of Unicode text. +The nine [Bidirectional explicit formatting](https://www.unicode.org/reports/tr9/#Bidirectional_Character_Types) +characters `\u202a - \u202e` and `\u2066 - \u2069` (inclusive) are forbidden +from appearing in source files. Note that they can be represented using +unicode escapes in string and character literals. + The program text is tokenized as described in this chapter. See the last section for special support for XML literals, which are parsed in _XML mode_. diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 6ece538e2ff1..65eb47049519 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -8,6 +8,11 @@ chapter: 13 The following descriptions of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. +The nine [Bidirectional explicit formatting](https://www.unicode.org/reports/tr9/#Bidirectional_Character_Types) +characters `\u202a - \u202e` and `\u2066 - \u2069` (inclusive) are forbidden +from appearing in source files. Note that they can be represented using +unicode escapes in string and character literals. + ## Lexical Syntax The lexical syntax of Scala is given by the following grammar in EBNF form: From 33ecb937fe03985d0a4c8ecd9f4829bb0b5c3089 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 31 Oct 2022 10:27:12 -0700 Subject: [PATCH 049/261] Upgrade -Vprint doc and default to typer --- src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6a3a812f21d8..e551cfec3c30 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -499,7 +499,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val showPhases = BooleanSetting("-Vphases", "Print a synopsis of compiler phases.") .withAbbreviation("-Xshow-phases") val Yposdebug = BooleanSetting("-Vpos", "Trace position validation.") withAbbreviation "-Ypos-debug" - val Xprint = PhasesSetting("-Vprint", "Print out program after") + val Xprint = PhasesSetting("-Vprint", "Print out program after (or ~phase for before and after)", "typer") .withAbbreviation("-Xprint") val Xprintpos = BooleanSetting("-Vprint-pos", "Print tree positions, as offsets.") .withAbbreviation("-Xprint-pos") From 78ad9c923a39db9fa68f02eafd0565e6bc85e206 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 1 Nov 2022 11:46:38 +0100 Subject: [PATCH 050/261] Upgrade to asm 9.4, for JDK20 support --- project/ScalaOptionParser.scala | 2 +- .../backend/jvm/analysis/BackendUtils.scala | 1 + .../nsc/settings/StandardScalaSettings.scala | 2 +- src/intellij/scala.ipr.SAMPLE | 28 +++++++++---------- versions.properties | 2 +- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 89d11246b11f..a9954f61e738 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -140,5 +140,5 @@ object ScalaOptionParser { private def scaladocPathSettingNames = List("-doc-root-content", "-diagrams-dot-path") private def scaladocMultiStringSettingNames = List("-doc-external-doc") - private val targetSettingNames = (8 to 19).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList + private val targetSettingNames = (8 to 20).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index 05444337a241..a73f1708efcd 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -83,6 +83,7 @@ abstract class BackendUtils extends PerRunInit { case "17" => asm.Opcodes.V17 case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 + case "20" => asm.Opcodes.V20 // to be continued... }) diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 10e9ab81ff22..610429d9e04f 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -103,7 +103,7 @@ object StandardScalaSettings { val MaxTargetVersion = ScalaVersion(javaSpecVersion) match { case SpecificScalaVersion(1, minor, _, _) => minor case SpecificScalaVersion(major, _, _, _) => major - case _ => 19 + case _ => 20 } private val AllTargetVersions = (MinTargetVersion to MaxTargetVersion).map(_.toString).to(List) diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index 7d917aa7f815..85c4cef274f1 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -232,7 +232,7 @@ - + @@ -243,7 +243,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -287,7 +287,7 @@ - + @@ -296,14 +296,14 @@ - + - + @@ -312,7 +312,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -457,7 +457,7 @@ - + @@ -467,7 +467,7 @@ - + @@ -498,7 +498,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -525,7 +525,7 @@ - + diff --git a/versions.properties b/versions.properties index 4b2572e1c19a..1815121788da 100644 --- a/versions.properties +++ b/versions.properties @@ -6,7 +6,7 @@ starr.version=2.13.10 # - scala-compiler: jline (% "optional") # Other usages: # - scala-asm: jar content included in scala-compiler -scala-asm.version=9.3.0-scala-1 +scala-asm.version=9.4.0-scala-1 # jna.version must be updated together with jline-terminal-jna jline.version=3.21.0 From f5e02c85e01edf3a34fcaea493d733ffb1f11803 Mon Sep 17 00:00:00 2001 From: Philippus Date: Sat, 8 Oct 2022 20:55:30 +0200 Subject: [PATCH 051/261] Upgrade to asm 9.4, for JDK20 support --- project/ScalaOptionParser.scala | 2 +- .../backend/jvm/analysis/BackendUtils.scala | 1 + .../nsc/settings/StandardScalaSettings.scala | 2 +- src/intellij/scala.ipr.SAMPLE | 26 +++++++++---------- .../scala/tools/nsc/settings/TargetTest.scala | 5 +++- versions.properties | 2 +- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 35c7b4cfda3e..2a4cab358f87 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -126,5 +126,5 @@ object ScalaOptionParser { private def scaladocPathSettingNames = List("-doc-root-content", "-diagrams-dot-path") private def scaladocMultiStringSettingNames = List("-doc-external-doc") - private val targetSettingNames = (5 to 19).flatMap(v => s"$v" :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList + private val targetSettingNames = (5 to 20).flatMap(v => s"$v" :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index 6f58a66b068d..9c12706f4b0f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -89,6 +89,7 @@ abstract class BackendUtils extends PerRunInit { case "17" => asm.Opcodes.V17 case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 + case "20" => asm.Opcodes.V20 // to be continued... }) diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 91a4632b81b9..01666cb2caf1 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -115,7 +115,7 @@ object StandardScalaSettings { val MaxTargetVersion = ScalaVersion(javaSpecVersion) match { case SpecificScalaVersion(1, minor, _, _) => minor case SpecificScalaVersion(major, _, _, _) => major - case _ => 19 + case _ => 20 } val MaxSupportedTargetVersion = 8 val DefaultTargetVersion = "8" diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index bed38cec30cd..7636da684a44 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -231,7 +231,7 @@ - + @@ -250,7 +250,7 @@ - + @@ -262,7 +262,7 @@ - + @@ -280,7 +280,7 @@ - + @@ -290,7 +290,7 @@ - + @@ -317,7 +317,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -340,7 +340,7 @@ - + @@ -350,7 +350,7 @@ - + @@ -503,7 +503,7 @@ - + @@ -516,7 +516,7 @@ - + @@ -527,7 +527,7 @@ - + @@ -552,7 +552,7 @@ - + diff --git a/test/junit/scala/tools/nsc/settings/TargetTest.scala b/test/junit/scala/tools/nsc/settings/TargetTest.scala index cf396b68cd7c..a599fa9fd2d1 100644 --- a/test/junit/scala/tools/nsc/settings/TargetTest.scala +++ b/test/junit/scala/tools/nsc/settings/TargetTest.scala @@ -95,7 +95,10 @@ class TargetTest { check("-target:jvm-19", "8", "19") check("-target:19", "8", "19") - checkFail("-target:jvm-20") // not yet... + check("-target:jvm-20", "8", "20") + check("-target:20", "8", "20") + + checkFail("-target:jvm-21") // not yet... checkFail("-target:jvm-3000") // not in our lifetime checkFail("-target:msil") // really? diff --git a/versions.properties b/versions.properties index 2bd815d40165..4d906bd1bd78 100644 --- a/versions.properties +++ b/versions.properties @@ -21,5 +21,5 @@ scala.binary.version=2.12 scala-xml.version.number=2.1.0 scala-parser-combinators.version.number=1.0.7 scala-swing.version.number=2.0.3 -scala-asm.version=9.3.0-scala-1 +scala-asm.version=9.4.0-scala-1 jline.version=2.14.6 From 48eb26325d49a5489154df7e04386423a9670b63 Mon Sep 17 00:00:00 2001 From: Anselm von Wangenheim Date: Mon, 31 Oct 2022 14:42:06 +0100 Subject: [PATCH 052/261] construct URL in manifestAt directly Prevoisly, calling `.getResource` sometimes returned an URL to a different jar, if there was one on classpath containing a manifest. This was because `getResource` delegates to the parent CL first, and only calls `findResource` if no manifest was found. As `findResource` is `protected` in `ClassLoader` and only made `public` by `java.net.URLClassLoader`, we resort to creating the URL ourselves instead. fixes scala/bug#12677 --- test/junit/scala/reflect/io/ZipArchiveTest.scala | 5 ++--- .../tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/test/junit/scala/reflect/io/ZipArchiveTest.scala b/test/junit/scala/reflect/io/ZipArchiveTest.scala index ec7ede4348b6..0a5e70fb8e7e 100644 --- a/test/junit/scala/reflect/io/ZipArchiveTest.scala +++ b/test/junit/scala/reflect/io/ZipArchiveTest.scala @@ -8,7 +8,6 @@ import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream} import org.junit.Assert._ import org.junit.Test -import scala.reflect.internal.util.ScalaClassLoader import scala.tools.testkit.AssertUtil._ import scala.tools.testkit.ForDeletion import scala.tools.testkit.Releasables._ @@ -50,12 +49,12 @@ class ZipArchiveTest { assertThrown[IOException](_.getMessage.contains(f.toString))(fza.iterator) } - private def manifestAt(location: URI): URL = ScalaClassLoader.fromURLs(List(location.toURL), null).getResource("META-INF/MANIFEST.MF"); + private def manifestAt(location: Path): URL = URI.create(s"jar:file:$location!/META-INF/MANIFEST.MF").toURL // ZipArchive.fromManifestURL(URL) @Test def `manifest resources just works`(): Unit = { val jar = createTestJar() - Using.resources(ForDeletion(jar), new ManifestResources(manifestAt(jar.toUri))) { (_, archive) => + Using.resources(ForDeletion(jar), new ManifestResources(manifestAt(jar.toAbsolutePath))) { (_, archive) => val it = archive.iterator assertTrue(it.hasNext) val f = it.next() diff --git a/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala b/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala index f152d27473f4..fc71bf4cde6e 100644 --- a/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala +++ b/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala @@ -6,7 +6,6 @@ import org.junit.Test import java.net.{URI, URL} import java.nio.file._ import java.nio.file.attribute.FileTime -import scala.reflect.internal.util.ScalaClassLoader import scala.reflect.io.AbstractFile import scala.tools.testkit.ForDeletion import scala.tools.testkit.Releasables._ @@ -82,10 +81,10 @@ class ZipAndJarFileLookupFactoryTest { () } } - def manifestAt(location: URI): URL = ScalaClassLoader.fromURLs(List(location.toURL), null).getResource("META-INF/MANIFEST.MF"); + def manifestAt(location: Path): URL = URI.create(s"jar:file:$location!/META-INF/MANIFEST.MF").toURL val j = createTestJar(); - Using.resources(ForDeletion(j), new ManifestResources(manifestAt(j.toUri)), new CloseableRegistry) { (_, archive, closeableRegistry) => + Using.resources(ForDeletion(j), new ManifestResources(manifestAt(j.toAbsolutePath)), new CloseableRegistry) { (_, archive, closeableRegistry) => val settings = new Settings val cp = ZipAndJarClassPathFactory.create(archive, settings, closeableRegistry) assertTrue(cp.findClass("foo").isDefined) From 07e4857608afbbddcb94206e754edd4f23064915 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 2 Nov 2022 16:42:20 +0100 Subject: [PATCH 053/261] GitHub Actions: allow manual runs as per * https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow * https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad09b5cc28f3..3ef580e00ae5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: Scala Merge CI on: push: branches: ['2.*.x'] + workflow_dispatch: defaults: run: From 6fac235a2da09755a067d8ec74ecd0252722504a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 3 Nov 2022 03:44:39 -0700 Subject: [PATCH 054/261] Test roundtrips path.toUri.getPath for windows --- test/junit/scala/reflect/io/ZipArchiveTest.scala | 2 +- .../tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/junit/scala/reflect/io/ZipArchiveTest.scala b/test/junit/scala/reflect/io/ZipArchiveTest.scala index 0a5e70fb8e7e..aaa14018b75a 100644 --- a/test/junit/scala/reflect/io/ZipArchiveTest.scala +++ b/test/junit/scala/reflect/io/ZipArchiveTest.scala @@ -49,7 +49,7 @@ class ZipArchiveTest { assertThrown[IOException](_.getMessage.contains(f.toString))(fza.iterator) } - private def manifestAt(location: Path): URL = URI.create(s"jar:file:$location!/META-INF/MANIFEST.MF").toURL + private def manifestAt(location: Path): URL = URI.create(s"jar:file:${location.toUri.getPath}!/META-INF/MANIFEST.MF").toURL // ZipArchive.fromManifestURL(URL) @Test def `manifest resources just works`(): Unit = { diff --git a/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala b/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala index fc71bf4cde6e..dee4611ed227 100644 --- a/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala +++ b/test/junit/scala/tools/nsc/classpath/ZipAndJarFileLookupFactoryTest.scala @@ -81,7 +81,7 @@ class ZipAndJarFileLookupFactoryTest { () } } - def manifestAt(location: Path): URL = URI.create(s"jar:file:$location!/META-INF/MANIFEST.MF").toURL + def manifestAt(location: Path): URL = URI.create(s"jar:file:${location.toUri.getPath}!/META-INF/MANIFEST.MF").toURL val j = createTestJar(); Using.resources(ForDeletion(j), new ManifestResources(manifestAt(j.toAbsolutePath)), new CloseableRegistry) { (_, archive, closeableRegistry) => From 3a68f1b54be7cb206c846a6af8706de7bd17976e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 3 Nov 2022 07:34:49 -0700 Subject: [PATCH 055/261] Tweak test for readability --- test/files/run/t1406b.check | 9 --------- test/files/run/t1406b.scala | 25 +++++++++++++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 test/files/run/t1406b.check diff --git a/test/files/run/t1406b.check b/test/files/run/t1406b.check deleted file mode 100644 index 50a0e9217169..000000000000 --- a/test/files/run/t1406b.check +++ /dev/null @@ -1,9 +0,0 @@ -C(84) -C(1764) -C(1764) -C(1806) -C(1806) -C(3528) -C(3528) -C(1806) -C(3528) diff --git a/test/files/run/t1406b.scala b/test/files/run/t1406b.scala index ff16cd296478..9bc32d3246e5 100644 --- a/test/files/run/t1406b.scala +++ b/test/files/run/t1406b.scala @@ -8,16 +8,21 @@ case class C(n: Int) { def *(c: C): C = C(n * c.n) def +(c: C): C = C(n + c.n) } + object Test extends App { + val Sum = 84 + val Product = 1764 + val ProductSum = 1806 + val SumProduct = 3528 val c, d = C(42) - println(c + d) - println(c * d) - println(c ☀ d) - println(c * d + d) - println(c ☀ d + d) - println(c ☀= d + d) // assignment op is low precedence - println(c 𐀀 d + d) // the first one, letter should be low precedence - println(c 🌀d + d) // the second one, cyclone should be high precedence - println(c 🌀= d + d) // the second one, cyclone should be high precedence + def assertEquals(expected: Int, actual: C) = assert(expected == actual.n) + assertEquals(Sum, c + d) + assertEquals(Product, c * d) + assertEquals(Product, c ☀ d) + assertEquals(ProductSum, c * d + d) + assertEquals(ProductSum, c ☀ d + d) + assertEquals(SumProduct, c ☀= d + d) // assignment op is low precedence + assertEquals(SumProduct, c 𐀀 d + d) // the first one, letter should be low precedence + assertEquals(ProductSum, c 🌀d + d) // the second one, cyclone should be high precedence + assertEquals(SumProduct, c 🌀= d + d) // assignment op is low precedence } - From 669bba35bcada8d764f74af2b1b6ccd145c1346d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 1 Nov 2022 07:28:15 -0700 Subject: [PATCH 056/261] Restore IterableWrapper equals and hashCode --- .../convert/JavaCollectionWrappers.scala | 10 ++- .../scala/collection/convert/EqualsTest.scala | 78 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/junit/scala/collection/convert/EqualsTest.scala diff --git a/src/library/scala/collection/convert/JavaCollectionWrappers.scala b/src/library/scala/collection/convert/JavaCollectionWrappers.scala index 49d6596b1a44..29c3dcbac5db 100644 --- a/src/library/scala/collection/convert/JavaCollectionWrappers.scala +++ b/src/library/scala/collection/convert/JavaCollectionWrappers.scala @@ -56,7 +56,15 @@ private[collection] object JavaCollectionWrappers extends Serializable { } @SerialVersionUID(3L) - class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable + class IterableWrapper[A](val underlying: Iterable[A]) extends ju.AbstractCollection[A] with IterableWrapperTrait[A] with Serializable { + import scala.runtime.Statics._ + override def equals(other: Any): Boolean = + other match { + case other: IterableWrapper[_] => underlying.equals(other.underlying) + case _ => false + } + override def hashCode = finalizeHash(mix(mix(0xcafebabe, "IterableWrapper".hashCode), anyHash(underlying)), 1) + } @SerialVersionUID(3L) class JIterableWrapper[A](val underlying: jl.Iterable[A]) diff --git a/test/junit/scala/collection/convert/EqualsTest.scala b/test/junit/scala/collection/convert/EqualsTest.scala new file mode 100644 index 000000000000..b3f9ae17176b --- /dev/null +++ b/test/junit/scala/collection/convert/EqualsTest.scala @@ -0,0 +1,78 @@ + +package scala.collection.convert + +import org.junit.Test +import org.junit.Assert._ + +import scala.jdk.CollectionConverters._ +import JavaCollectionWrappers._ + +import java.util.{AbstractList, AbstractSet, List => JList, Set => JSet} + +class JTestList(vs: Int*) extends AbstractList[Int] { + def this() = this(Nil: _*) + override def size = vs.size + override def get(i: Int) = vs(i) +} +class JTestSet(vs: Int*) extends AbstractSet[Int] { + def this() = this(Nil: _*) + require(vs.toSet.size == vs.size) + override def size = vs.size + override def iterator = vs.iterator.asJava +} + +/** Test that collection wrappers forward equals and hashCode where appropriate. */ +class EqualsTest { + + def jlstOf(vs: Int*): JList[Int] = new JTestList(vs: _*) + def jsetOf(vs: Int*): JSet[Int] = new JTestSet(vs: _*) + + // Seq extending AbstractList inherits equals + + @Test def `List as JList has equals`: Unit = { + val list = List(1, 2, 3) + val jlst = new SeqWrapper(list) + assertEquals(jlstOf(1, 2, 3), jlst) + assertEquals(jlst, jlstOf(1, 2, 3)) + assertTrue(jlst == jlstOf(1, 2, 3)) + assertEquals(jlst.hashCode, jlst.hashCode) + } + + @Test def `Set as JSet has equals`: Unit = { + val set = Set(1, 2, 3) + val jset = new SetWrapper(set) + assertEquals(jsetOf(1, 2, 3), jset) + assertEquals(jset, jsetOf(1, 2, 3)) + assertTrue(jset == jsetOf(1, 2, 3)) + assertEquals(jset.hashCode, jset.hashCode) + } + + @Test def `Map as JMap has equals`: Unit = { + val map = Map(1 -> "one", 2 -> "two", 3 -> "three") + val jmap = new MapWrapper(map) + assertEquals(jmap, jmap) + } + + @Test def `Anything as Collection is equal to Anything`: Unit = { + def set = Set(1, 2, 3) + def jset = new IterableWrapper(set) + assertTrue(jset == jset) + assertEquals(jset, jset) + assertNotEquals(jset, set) + assertEquals(jset.hashCode, jset.hashCode) + } + + @Test def `Iterator wrapper does not compare equal`: Unit = { + def it = List(1, 2, 3).iterator + def jit = new IteratorWrapper(it) + assertNotEquals(jit, jit) + assertNotEquals(jit.hashCode, jit.hashCode) + } + + @Test def `Anything.asScala Iterable has case equals`: Unit = { + def vs = jlstOf(42, 27, 37) + def it = new JListWrapper(vs) + assertEquals(it, it) + assertEquals(it.hashCode, it.hashCode) + } +} From 98211ada81c327d1e672d7b22ec04c26670699f9 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sat, 5 Nov 2022 00:57:13 +0000 Subject: [PATCH 057/261] Update sbt-header to 5.8.0 in 2.12.x --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index c1c09a6780a6..89ad07e209a6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -30,6 +30,6 @@ concurrentRestrictions in Global := Seq( Tags.limitAll(1) // workaround for https://github.com/sbt/sbt/issues/2970 ) -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.8.0") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") From a4a2031e9e64f758df2be3cb14a88b700d959336 Mon Sep 17 00:00:00 2001 From: "hepin.p" Date: Sat, 8 Oct 2022 16:01:38 +0800 Subject: [PATCH 058/261] =lib Make use of CompletionStage#handle instead of CompletionStage#whenComplete for better performance. --- project/MimaFilters.scala | 7 +++++++ .../scala/concurrent/impl/FutureConvertersImpl.scala | 4 ++-- src/library/scala/jdk/javaapi/FutureConverters.scala | 10 +++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index cdc43bacc57c..4e801b2cdf6c 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -31,6 +31,13 @@ object MimaFilters extends AutoPlugin { ProblemFilters.exclude[DirectMissingMethodProblem]("scala.Predef#ArrayCharSequence.isEmpty"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.ArrayCharSequence.isEmpty"), + // KEEP: make use of CompletionStage#handle to get a better performance than CompletionStage#whenComplete. + ProblemFilters.exclude[MissingTypesProblem]("scala.concurrent.impl.FutureConvertersImpl$P"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.apply"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.accept"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), ) override val buildSettings = Seq( diff --git a/src/library/scala/concurrent/impl/FutureConvertersImpl.scala b/src/library/scala/concurrent/impl/FutureConvertersImpl.scala index e73b4bd5bfca..0980ade5a5a7 100644 --- a/src/library/scala/concurrent/impl/FutureConvertersImpl.scala +++ b/src/library/scala/concurrent/impl/FutureConvertersImpl.scala @@ -91,8 +91,8 @@ private[scala] object FutureConvertersImpl { override def toString(): String = super[CompletableFuture].toString } - final class P[T](val wrapped: CompletionStage[T]) extends DefaultPromise[T] with BiConsumer[T, Throwable] { - override def accept(v: T, e: Throwable): Unit = { + final class P[T](val wrapped: CompletionStage[T]) extends DefaultPromise[T] with BiFunction[T, Throwable, Unit] { + override def apply(v: T, e: Throwable): Unit = { if (e == null) success(v) else failure(e) } diff --git a/src/library/scala/jdk/javaapi/FutureConverters.scala b/src/library/scala/jdk/javaapi/FutureConverters.scala index 132b2feb03fb..d8888d207b4b 100644 --- a/src/library/scala/jdk/javaapi/FutureConverters.scala +++ b/src/library/scala/jdk/javaapi/FutureConverters.scala @@ -12,10 +12,10 @@ package scala.jdk.javaapi -import java.util.concurrent.CompletionStage - +import java.util.concurrent.{CompletableFuture, CompletionStage} import scala.concurrent.impl.FutureConvertersImpl.{CF, P} import scala.concurrent.{ExecutionContext, Future} +import scala.util.Success /** This object contains methods that convert between Scala [[scala.concurrent.Future]] and Java [[java.util.concurrent.CompletionStage]]. * @@ -70,9 +70,13 @@ object FutureConverters { case cf: CF[T] => cf.wrapped // in theory not safe (could be `class C extends Future[A] with CompletionStage[B]`): case f: Future[T @unchecked] => f + case cf: CompletableFuture[T @unchecked] if cf.isDone && !cf.isCompletedExceptionally => + val p = new P[T](cs) + p.tryComplete(Success(cf.join())) + p.future case _ => val p = new P[T](cs) - cs whenComplete p + cs.handle(p) p.future } } From 1e27f99e66e67b1ce48299ebf98a4f76a7885e5a Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 8 Nov 2022 14:46:42 +0100 Subject: [PATCH 059/261] Fix interaction between string switching and async ``` def testSwitch(s: String) = optionally { s match { case "" => "" case p => value(Some(p)) p } } ``` Pattern translation introduces a local for the scrutinee (`x1`) and replaces uses of `p` in the default case by `x1`. After patmat: ``` case val x1: String = s; x1 match { case "" => "" case _ => { scala.tools.partest.async.OptionAwait.value[String](new Some[String](x1)); x1 } } ``` The `x1` variable is accessed in different async stages, and therefore transformed into a state machine field. After async: ``` (stateMachine$async.this.x1: String) match { case "" => ... ... case _ => ... } ``` This breaks the translation of string switches in `CleanUp`, which assumes the scrutinee AST is a `Literal` or an `Ident`. The fix is to introduce another local variable if needed. --- .../scala/tools/nsc/transform/CleanUp.scala | 157 +++++++++--------- test/async/run/string-switch-async.scala | 19 +++ 2 files changed, 102 insertions(+), 74 deletions(-) create mode 100644 test/async/run/string-switch-async.scala diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 96c0bd21b29d..60758c58c54a 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -398,85 +398,94 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { } private def transformStringSwitch(sw: Match): Tree = { import CODE._ - // these assumptions about the shape of the tree are justified by the codegen in MatchOptimization - val Match(Typed(selTree, _), cases) = sw: @unchecked - def selArg = selTree match { - case x: Ident => REF(x.symbol) - case x: Literal => x - case x => throw new MatchError(x) - } - val newSel = selTree match { - case x: Ident => atPos(x.symbol.pos)(IF (x.symbol OBJ_EQ NULL) THEN ZERO ELSE selArg.OBJ_##) - case x: Literal => atPos(x.pos) (if (x.value.value == null) ZERO else selArg.OBJ_##) - case x => throw new MatchError(x) - } - val restpe = sw.tpe - val resUnit = restpe =:= UnitTpe - val swPos = sw.pos.focus - - /* From this: - * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3 } - * Generate this: - * string.## match { - * case 2031744 => - * if ("AaAa" equals string) goto matchEnd (1) - * else if ("BBBB" equals string) goto case2 - * else goto defaultCase - * case 99 => - * if ("c" equals string) goto case2 - * else goto defaultCase - * case _ => goto defaultCase - * } - * case2: goto matchEnd (2) - * defaultCase: goto matchEnd (3) // or `goto matchEnd (throw new MatchError(string))` if no default was given - * matchEnd(res: Int): res - * Extra labels are added for alternative patterns branches, since multiple branches in the - * resulting switch may need to correspond to a single case body. - */ + // tree shape assumption justified by the codegen in MatchOptimization + val Match(Typed(selTree0, _), cases) = sw: @unchecked + // usually `selTree0` is an `Ident` or `Literal`, but Xasync may transform the scrutinee local into a state + // machine field (scala/bug#12686). `evalOnce` introduces another local if needed (not for Ident / Literal). + gen.evalOnce(selTree0, currentOwner, unit) { selTree => + def selArg = selTree() match { + case x: Ident => REF(x.symbol) + case x: Literal => x + case x => throw new MatchError(x) + } - val labels = mutable.ListBuffer.empty[LabelDef] - var defaultCaseBody = Throw(New(MatchErrorClass.tpe_*, selArg)): Tree + val newSel = selTree() match { + case x: Ident => atPos(x.symbol.pos)(IF(x.symbol OBJ_EQ NULL) THEN ZERO ELSE selArg.OBJ_##) + case x: Literal => atPos(x.pos)(if (x.value.value == null) ZERO else selArg.OBJ_##) + case x => throw new MatchError(x) + } + val restpe = sw.tpe + val resUnit = restpe =:= UnitTpe + val swPos = sw.pos.focus + + /* From this: + * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3 } + * Generate this: + * string.## match { + * case 2031744 => + * if ("AaAa" equals string) goto matchEnd (1) + * else if ("BBBB" equals string) goto case2 + * else goto defaultCase + * case 99 => + * if ("c" equals string) goto case2 + * else goto defaultCase + * case _ => goto defaultCase + * } + * case2: goto matchEnd (2) + * defaultCase: goto matchEnd (3) // or `goto matchEnd (throw new MatchError(string))` if no default was given + * matchEnd(res: Int): res + * Extra labels are added for alternative patterns branches, since multiple branches in the + * resulting switch may need to correspond to a single case body. + */ - def LABEL(name: String) = currentOwner.newLabel(unit.freshTermName(name), swPos).setFlag(SYNTH_CASE_FLAGS) - def newCase() = LABEL( "case").setInfo(MethodType(Nil, restpe)) - val defaultCase = LABEL("defaultCase").setInfo(MethodType(Nil, restpe)) - val matchEnd = LABEL("matchEnd").tap { lab => - // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of - // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending. - lab.setInfo(MethodType(if (resUnit) Nil else List(lab.newSyntheticValueParam(restpe)), restpe)) - } - def goto(sym: Symbol, params: Tree*) = REF(sym) APPLY (params: _*) - def gotoEnd(body: Tree) = if (resUnit) BLOCK(body, goto(matchEnd)) else goto(matchEnd, body) - - val casesByHash = cases.flatMap { - case cd@CaseDef(StringsPattern(strs), _, body) => - val jump = newCase() // always create a label so when its used it matches the source case (e.g. `case4()`) - strs match { - case str :: Nil => List((str, gotoEnd(body), cd.pat.pos)) - case _ => - labels += LabelDef(jump, Nil, gotoEnd(body)) - strs.map((_, goto(jump), cd.pat.pos)) - } - case cd if isDefaultCase(cd) => defaultCaseBody = gotoEnd(cd.body); None - case cd => globalError(s"unhandled in switch: $cd"); None - }.groupBy(_._1.##) - - val newCases = casesByHash.toList.sortBy(_._1).map { - case (hash, cases) => - val newBody = cases.foldRight(atPos(swPos)(goto(defaultCase): Tree)) { - case ((null, rhs, pos), next) => atPos(pos)(IF (NULL OBJ_EQ selArg) THEN rhs ELSE next) - case ((str, rhs, pos), next) => atPos(pos)(IF (LIT(str) OBJ_== selArg) THEN rhs ELSE next) - } - CASE(LIT(hash)) ==> newBody - } + val labels = mutable.ListBuffer.empty[LabelDef] + var defaultCaseBody = Throw(New(MatchErrorClass.tpe_*, selArg)): Tree - labels += LabelDef(defaultCase, Nil, defaultCaseBody) - labels += LabelDef(matchEnd, matchEnd.info.params, matchEnd.info.params.headOption.fold(UNIT: Tree)(REF)) + def LABEL(name: String) = currentOwner.newLabel(unit.freshTermName(name), swPos).setFlag(SYNTH_CASE_FLAGS) - val stats = Match(newSel, newCases :+ (DEFAULT ==> goto(defaultCase))) :: labels.toList + def newCase() = LABEL("case").setInfo(MethodType(Nil, restpe)) - val res = Block(stats: _*) - typedWithPos(sw.pos)(res) + val defaultCase = LABEL("defaultCase").setInfo(MethodType(Nil, restpe)) + val matchEnd = LABEL("matchEnd").tap { lab => + // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of + // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending. + lab.setInfo(MethodType(if (resUnit) Nil else List(lab.newSyntheticValueParam(restpe)), restpe)) + } + + def goto(sym: Symbol, params: Tree*) = REF(sym) APPLY (params: _*) + + def gotoEnd(body: Tree) = if (resUnit) BLOCK(body, goto(matchEnd)) else goto(matchEnd, body) + + val casesByHash = cases.flatMap { + case cd@CaseDef(StringsPattern(strs), _, body) => + val jump = newCase() // always create a label so when its used it matches the source case (e.g. `case4()`) + strs match { + case str :: Nil => List((str, gotoEnd(body), cd.pat.pos)) + case _ => + labels += LabelDef(jump, Nil, gotoEnd(body)) + strs.map((_, goto(jump), cd.pat.pos)) + } + case cd if isDefaultCase(cd) => defaultCaseBody = gotoEnd(cd.body); None + case cd => globalError(s"unhandled in switch: $cd"); None + }.groupBy(_._1.##) + + val newCases = casesByHash.toList.sortBy(_._1).map { + case (hash, cases) => + val newBody = cases.foldRight(atPos(swPos)(goto(defaultCase): Tree)) { + case ((null, rhs, pos), next) => atPos(pos)(IF(NULL OBJ_EQ selArg) THEN rhs ELSE next) + case ((str, rhs, pos), next) => atPos(pos)(IF(LIT(str) OBJ_== selArg) THEN rhs ELSE next) + } + CASE(LIT(hash)) ==> newBody + } + + labels += LabelDef(defaultCase, Nil, defaultCaseBody) + labels += LabelDef(matchEnd, matchEnd.info.params, matchEnd.info.params.headOption.fold(UNIT: Tree)(REF)) + + val stats = Match(newSel, newCases :+ (DEFAULT ==> goto(defaultCase))) :: labels.toList + + val res = Block(stats: _*) + typedWithPos(sw.pos)(res) + } } // transform scrutinee of all matches to switchable types (ints, strings) diff --git a/test/async/run/string-switch-async.scala b/test/async/run/string-switch-async.scala new file mode 100644 index 000000000000..466f7e3abac4 --- /dev/null +++ b/test/async/run/string-switch-async.scala @@ -0,0 +1,19 @@ +// scalac: -Xasync + +import scala.tools.partest.async.OptionAwait._ +import org.junit.Assert._ + +object Test { + def main(args: Array[String]): Unit = { + assertEquals(Some(""), testSwitch("")) + assertEquals(Some("aa"), testSwitch("a")) + } + + private def testSwitch(s: String) = optionally { + s match { + case "" => "" + case p => + value(Some(p)) + p + } + } +} From 13967af93509b4c6294de6a41ceaef92b0687c88 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 8 Nov 2022 16:40:06 +0100 Subject: [PATCH 060/261] Fix criterion # in value class error message --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/neg/t6534.check | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 45f042e81e92..aaf4eabfc9f1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1466,7 +1466,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (stat.symbol.isAuxiliaryConstructor) notAllowed("secondary constructor") else if (isValueClass && (name == nme.equals_ || name == nme.hashCode_) && !stat.symbol.isSynthetic) - notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.") + notAllowed(s"redefinition of $name method. See SIP-15, criterion 5.") else if (stat.symbol != null && stat.symbol.isParamAccessor) notAllowed("additional parameter") checkEphemeralDeep.traverse(rhs) diff --git a/test/files/neg/t6534.check b/test/files/neg/t6534.check index ae99eac73bb2..c2d57178c6ed 100644 --- a/test/files/neg/t6534.check +++ b/test/files/neg/t6534.check @@ -1,10 +1,10 @@ -t6534.scala:8: error: redefinition of equals method. See SIP-15, criterion 4. is not allowed in value class +t6534.scala:8: error: redefinition of equals method. See SIP-15, criterion 5. is not allowed in value class class Bippy3(val x: Int) extends AnyVal { override def equals(x: Any) = false } // error ^ -t6534.scala:9: error: redefinition of hashCode method. See SIP-15, criterion 4. is not allowed in value class +t6534.scala:9: error: redefinition of hashCode method. See SIP-15, criterion 5. is not allowed in value class class Bippy4(val x: Int) extends AnyVal { override def hashCode = -1 } // error ^ -t6534.scala:11: error: redefinition of equals method. See SIP-15, criterion 4. is not allowed in value class +t6534.scala:11: error: redefinition of equals method. See SIP-15, criterion 5. is not allowed in value class case class Bippy6(val x: Int) extends AnyVal { override def productPrefix = "Dingo" ; override def equals(x: Any) = false } // error ^ 3 errors From f2eee9cab074db68289ee1db42c28b8fb8a9818c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 4 Nov 2022 15:45:46 +1000 Subject: [PATCH 061/261] Reduce implicit search space for BuildFrom avoiding divergence BuildFrom (nee CanBuildFrom) is de-emphasised in 2.13 but remains for use by generic extension methods that abstract over different collection types. As part of the 2.13 redesign, implicit instances for `BuildFrom[SomeCollection]` where moved from the `SomeCollection` companions into the `BuildFrom` companion. This has an unfortunate side effect -- more implicits are in scope for implicit searches, which can incur longer search time and can also lead into divergent searches. Implicit search prunes candidates based on the "depoly"-ed type, which type params and value params with wildcards types in the implicit signature. The nested implicit search for implicit params occurs, which can guide inference of the type params. Only then is the true type known and compared against the expected type of the search. It seems that this "depoly" transform could be smarter by approximating type params to bounds. Perhaps this is harder than it sounds when you have things like F-bounded or mutually recursive type params. Instead of trying to fix that general problem, I've instead restated the bounds in the result type of relevant implicits in BuildFrom. This would be redundant, were it not for way that they are preseved through `depoly`. The new test case now compiles without divergence and the search considers only one candidate in detail. Previously, compile failed with a divergent error in `Ordering` implicits after about 80 steps. --- src/library/scala/collection/BuildFrom.scala | 9 +++-- .../scala/collection/BuildFromTest.scala | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/library/scala/collection/BuildFrom.scala b/src/library/scala/collection/BuildFrom.scala index e0e92a7e66a1..bc9c49d9493c 100644 --- a/src/library/scala/collection/BuildFrom.scala +++ b/src/library/scala/collection/BuildFrom.scala @@ -45,14 +45,14 @@ trait BuildFrom[-From, -A, +C] extends Any { self => object BuildFrom extends BuildFromLowPriority1 { /** Build the source collection type from a MapOps */ - implicit def buildFromMapOps[CC[X, Y] <: Map[X, Y] with MapOps[X, Y, CC, _], K0, V0, K, V]: BuildFrom[CC[K0, V0], (K, V), CC[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { + implicit def buildFromMapOps[CC[X, Y] <: Map[X, Y] with MapOps[X, Y, CC, _], K0, V0, K, V]: BuildFrom[CC[K0, V0] with Map[K0, V0], (K, V), CC[K, V] with Map[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { //TODO: Reuse a prototype instance def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: MapOps[K0, V0, CC, _]).mapFactory.newBuilder[K, V] def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: MapOps[K0, V0, CC, _]).mapFactory.from(it) } /** Build the source collection type from a SortedMapOps */ - implicit def buildFromSortedMapOps[CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], K0, V0, K : Ordering, V]: BuildFrom[CC[K0, V0], (K, V), CC[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { + implicit def buildFromSortedMapOps[CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, CC, _], K0, V0, K : Ordering, V]: BuildFrom[CC[K0, V0] with SortedMap[K0, V0], (K, V), CC[K, V] with SortedMap[K, V]] = new BuildFrom[CC[K0, V0], (K, V), CC[K, V]] { def newBuilder(from: CC[K0, V0]): Builder[(K, V), CC[K, V]] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.newBuilder[K, V] def fromSpecific(from: CC[K0, V0])(it: IterableOnce[(K, V)]): CC[K, V] = (from: SortedMapOps[K0, V0, CC, _]).sortedMapFactory.from(it) } @@ -92,7 +92,10 @@ object BuildFrom extends BuildFromLowPriority1 { trait BuildFromLowPriority1 extends BuildFromLowPriority2 { /** Build the source collection type from an Iterable with SortedOps */ - implicit def buildFromSortedSetOps[CC[X] <: SortedSet[X] with SortedSetOps[X, CC, _], A0, A : Ordering]: BuildFrom[CC[A0], A, CC[A]] = new BuildFrom[CC[A0], A, CC[A]] { + // Restating the upper bound of CC in the result type seems redundant, but it serves to prune the + // implicit search space for faster compilation and reduced change of divergence. See the compilation + // test in test/junit/scala/collection/BuildFromTest.scala and discussion in https://github.com/scala/scala/pull/10209 + implicit def buildFromSortedSetOps[CC[X] <: SortedSet[X] with SortedSetOps[X, CC, _], A0, A : Ordering]: BuildFrom[CC[A0] with SortedSet[A0], A, CC[A] with SortedSet[A]] = new BuildFrom[CC[A0], A, CC[A]] { def newBuilder(from: CC[A0]): Builder[A, CC[A]] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.newBuilder[A] def fromSpecific(from: CC[A0])(it: IterableOnce[A]): CC[A] = (from: SortedSetOps[A0, CC, _]).sortedIterableFactory.from(it) } diff --git a/test/junit/scala/collection/BuildFromTest.scala b/test/junit/scala/collection/BuildFromTest.scala index fedbe1b9042c..12fe6d9595b2 100644 --- a/test/junit/scala/collection/BuildFromTest.scala +++ b/test/junit/scala/collection/BuildFromTest.scala @@ -176,4 +176,41 @@ class BuildFromTest { immutable.LongMap: BuildFrom[_, (Long, String), immutable.LongMap[String]] mutable.LongMap: BuildFrom[_, (Long, String), mutable.LongMap[String]] mutable.AnyRefMap: BuildFrom[_, (String, String), mutable.AnyRefMap[String, String]] + + // Check that we don't get an implicit divergence in a futile part of the search tree: + { + sealed trait GPoint + sealed trait HNil extends GPoint + class HCons[H, +T <: GPoint] extends GPoint + abstract class ExtendsOrdered extends Ordered[ExtendsOrdered] + + + // In scala 2.13, this implicit search considers BuildFrom.buildFromSortedSetOps + // which looks for a dep. implicit of type Ordering[(Int, HCons[ExtendsOrdered, HNil])] + implicitly[collection.BuildFrom[Seq[Any], (Int, HCons[ExtendsOrdered, HNil]), Seq[(Int, HCons[ExtendsOrdered, HNil])]]] + + // + // In Scala 2.12, buildFromSortedSetOps is not a candidate because if it is in the companion object of + // the SortedSet heirarchy, which is not part of the implicit scope for this search. + // In 2.13, the implicit was moved to `object BuildFrom`, so _is_ considered + // + // The dependent implicit search: + // + // implicitly[(Int, HCons[ExtendsOrdered, HNil])] + // + // ... diverges on both Scala 2.12 and 2.13 + // + // error: diverging implicit expansion for type scala.math.Ordering.AsComparable[(Int, HCons[ExtendsOrdered,HNil])] + // starting with method orderingToOrdered in object Ordered + // + // Divergences in Ordering implicits are a long standing problem, but I always thought it too hard to + // fix while retaining source compatibility. + // + // Removing `extends Ordered[X]` avoids divergence, but I'm not sure why. I diffed the -Vtyper log but + // can't figure out why that is relevant. + // + // (In the original code, ExtendsOrdered was actually scala.Enumeration.Value, which does extends Ordered. + // + // + } } From 8f8a8d6ceead8bdd683e415a8daaf63fdf2a0ab6 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 27 Oct 2022 11:50:39 +0200 Subject: [PATCH 062/261] [backport] fix accessibility case for static java members Given ``` public class A { protected static class AI { } } public class B extends A { public static class BI extends AI { } } ``` The owner of `AI` is the module class `A$`, the owner of `BI` is the module class `B$`. When checking if the protected `AI` can be accessed in `B$`, we need to navigate from `B$` to `B`. The inheritance chain is not reflected in the module classes. --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 4 +++- test/files/pos/t12673/A.java | 4 ++++ test/files/pos/t12673/B.java | 6 ++++++ test/files/pos/t12673/Test.scala | 5 +++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t12673/A.java create mode 100644 test/files/pos/t12673/B.java create mode 100644 test/files/pos/t12673/Test.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 57c01dbf32cb..f73c8ef3f655 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -695,7 +695,9 @@ trait Contexts { self: Analyzer => private def isSubClassOrCompanion(sub: Symbol, base: Symbol) = sub.isNonBottomSubClass(base) || (sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)) || - (base.isJavaDefined && base.isModuleClass && sub.isNonBottomSubClass(base.linkedClassOfClass)) + (base.isJavaDefined && base.isModuleClass && ( + sub.isNonBottomSubClass(base.linkedClassOfClass) || + sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base.linkedClassOfClass))) /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. diff --git a/test/files/pos/t12673/A.java b/test/files/pos/t12673/A.java new file mode 100644 index 000000000000..faaf0d26761f --- /dev/null +++ b/test/files/pos/t12673/A.java @@ -0,0 +1,4 @@ +package compiletest.a; +public class A { + protected static class InnerParent { } +} diff --git a/test/files/pos/t12673/B.java b/test/files/pos/t12673/B.java new file mode 100644 index 000000000000..d94349074731 --- /dev/null +++ b/test/files/pos/t12673/B.java @@ -0,0 +1,6 @@ +package compiletest.b; +import compiletest.a.A; + +public class B extends A { + public static class InnerChild extends InnerParent { } +} diff --git a/test/files/pos/t12673/Test.scala b/test/files/pos/t12673/Test.scala new file mode 100644 index 000000000000..1f867027731b --- /dev/null +++ b/test/files/pos/t12673/Test.scala @@ -0,0 +1,5 @@ +package client + +object Client { + new compiletest.b.B.InnerChild +} From 1860378fc42a55a97aa0a84f67567f248d4b4738 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 10 Nov 2022 10:36:40 -1000 Subject: [PATCH 063/261] make warning in build go away --- project/Osgi.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Osgi.scala b/project/Osgi.scala index 016c19c422cd..ea0308992a67 100644 --- a/project/Osgi.scala +++ b/project/Osgi.scala @@ -3,7 +3,7 @@ package scala.build import aQute.bnd.osgi.Builder import aQute.bnd.osgi.Constants._ import java.util.jar.Attributes -import sbt._ +import sbt.{License => _, _} import sbt.Keys._ import collection.JavaConverters._ import VersionUtil.versionProperties From 35a857bb476aa3b0270ddf3a96dc38a8ad334e48 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 10 Nov 2022 11:16:06 -1000 Subject: [PATCH 064/261] in a Scalacheck test, aavoid nesting properties because doing so runs afoul of typelevel/scalacheck#677 --- .../tools/nsc/scaladoc/HtmlFactoryTest.scala | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala b/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala index 7dac92adb5c6..bc59340dbca7 100644 --- a/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala +++ b/test/scalacheck/scala/tools/nsc/scaladoc/HtmlFactoryTest.scala @@ -660,20 +660,16 @@ object HtmlFactoryTest extends Properties("HtmlFactory") { val files = createTemplates("basic.scala") //println(files) - property("class") = files.get("com/example/p1/Clazz.html") match { - case Some(node: scala.xml.Node) => { - property("implicit conversion") = - node.toString contains "implicit " - - property("gt4s") = - node.toString contains "title=\"gt4s: $colon$colon\"" - - property("gt4s of a deprecated method") = - node.toString contains "title=\"gt4s: $colon$colon$colon$colon. Deprecated: " - true - } - case _ => false + locally { + val node = files.get("com/example/p1/Clazz.html").get + property("implicit conversion") = + node.toString contains "implicit " + property("gt4s") = + node.toString contains "title=\"gt4s: $colon$colon\"" + property("gt4s of a deprecated method") = + node.toString contains "title=\"gt4s: $colon$colon$colon$colon. Deprecated: " } + property("package") = files.get("com/example/p1/index.html") != None property("package object") = files("com/example/p1/index.html") match { From 8777685be41860117e660850a763f888a64f19d3 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Tue, 8 Nov 2022 23:01:36 +0000 Subject: [PATCH 065/261] Update scalacheck to 1.17.0 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 4ffd348eee98..65d3538f807f 100644 --- a/build.sbt +++ b/build.sbt @@ -42,7 +42,7 @@ val scalaParserCombinatorsDep = scalaDep("org.scala-lang.modules", "scala-par // Non-Scala dependencies: val junitDep = "junit" % "junit" % "4.12" val junitInterfaceDep = "com.novocode" % "junit-interface" % "0.11" % Test -val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.14.3" % Test +val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.17.0" % Test val jolDep = "org.openjdk.jol" % "jol-core" % "0.16" val asmDep = "org.scala-lang.modules" % "scala-asm" % versionProps("scala-asm.version") val jlineDep = "jline" % "jline" % versionProps("jline.version") From 7495b7c5b5bb6227024699ae7ab764a9cb6fc893 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 12 Nov 2022 18:35:07 -0800 Subject: [PATCH 066/261] Faster diagnostic for unimplemented method stubs --- .../tools/nsc/typechecker/RefChecks.scala | 30 ++--- test/files/neg/t12691.check | 110 ++++++++++++++++++ test/files/neg/t12691.scala | 106 +++++++++++++++++ test/files/neg/t12691b.check | 109 +++++++++++++++++ test/files/neg/t12691b.scala | 105 +++++++++++++++++ 5 files changed, 447 insertions(+), 13 deletions(-) create mode 100644 test/files/neg/t12691.check create mode 100644 test/files/neg/t12691.scala create mode 100644 test/files/neg/t12691b.check create mode 100644 test/files/neg/t12691b.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index c8d9651d819e..a6be3eb01ae3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -572,7 +572,7 @@ abstract class RefChecks extends Transform { def checkNoAbstractMembers(): Unit = { val NoError = null.asInstanceOf[String] val EmptyDiagnostic = "" - def diagnose(member: Symbol, accessors: List[Symbol]): String = { + def diagnose(member: Symbol, accessors: List[Symbol], nonPrivateMembers: Scope, fastDiagnostics: Boolean): String = { val underlying = analyzer.underlyingSymbol(member) // TODO: don't use this method // Give a specific error message for abstract vars based on why it fails: @@ -585,21 +585,21 @@ abstract class RefChecks extends Transform { else if (member.isGetter && !isMultiple) "an abstract var requires a getter in addition to the setter" else "variables need to be initialized to be defined" } - else if (underlying.isMethod) { + else if (!fastDiagnostics && underlying.isMethod) { // Highlight any member that nearly matches: same name and arity, // but differs in one param or param list. val abstractParamLists = underlying.paramLists - val matchingArity = clazz.tpe.nonPrivateMembersAdmitting(VBRIDGE).filter { m => + val matchingArity = nonPrivateMembers.reverseIterator.filter { m => !m.isDeferred && m.name == underlying.name && sameLength(m.paramLists, abstractParamLists) && sumSize(m.paramLists, 0) == sumSize(abstractParamLists, 0) && sameLength(m.tpe.typeParams, underlying.tpe.typeParams) && !(m.isJavaDefined && m.hasFlag(JAVA_DEFAULTMETHOD)) - } + }.toList matchingArity match { // So far so good: only one candidate method - case Scope(concrete) => + case concrete :: Nil => val concreteParamLists = concrete.paramLists val aplIter = abstractParamLists.iterator.flatten val cplIter = concreteParamLists.iterator.flatten @@ -642,17 +642,18 @@ abstract class RefChecks extends Transform { } else EmptyDiagnostic } - def emitErrors(missing: List[Symbol]): Unit = { + def emitErrors(missing: List[Symbol], nonPrivateMembers: Scope): Unit = { + val fastDiagnostics = missing.lengthCompare(100) > 0 // Group missing members by the name of the underlying symbol, to consolidate getters and setters. val byName = missing.groupBy(_.name.getterName) // There may be 1 or more missing members declared in 1 or more parents. // If a single parent, the message names it. Otherwise, missing members are grouped by declaring class. val byOwner = missing.groupBy(_.owner).toList val announceOwner = byOwner.size > 1 - def membersStrings(members: List[Symbol]) = + def membersStrings(members: List[Symbol]) = { members.sortBy(_.name).flatMap { m => val accessors = byName.getOrElse(m.name.getterName, Nil) - val diagnostic = diagnose(m, accessors) + val diagnostic = diagnose(m, accessors, nonPrivateMembers, fastDiagnostics) if (diagnostic == NoError) Nil else { val s0a = infoString0(m, showLocation = false) @@ -665,16 +666,18 @@ abstract class RefChecks extends Transform { s"$s1 = ???$comment" :: Nil } } + } var count = 0 - val stubs = + def isMulti = count > 1 + def helpfulListing = byOwner.sortBy(_._1.name.toString).flatMap { case (owner, members) => val ms = membersStrings(members) :+ "" count += ms.size - 1 if (announceOwner) s"// Members declared in ${owner.fullName}" :: ms else ms }.init.map(s => s" $s\n").mkString - val isMulti = count > 1 - val singleParent = if (byOwner.size == 1 && byOwner.head._1 != clazz) s" member${if (isMulti) "s" else ""} of ${byOwner.head._1}" else "" + val stubs = helpfulListing + def singleParent = if (byOwner.size == 1 && byOwner.head._1 != clazz) s" member${if (isMulti) "s" else ""} of ${byOwner.head._1}" else "" val line0 = if (isMulti) s"Missing implementations for ${count}${val p = singleParent ; if (p.isEmpty) " members" else p}." else s"Missing implementation${val p = singleParent ; if (p.isEmpty) p else s" for$p"}:" @@ -689,10 +692,11 @@ abstract class RefChecks extends Transform { } (ps, qs) } + val nonPrivateMembers = clazz.info.nonPrivateMembersAdmitting(VBRIDGE) // Avoid extra allocations with reverseIterator. Filter for abstract members of interest, and bad abstract override. val (missing, abstractIncomplete): (List[Symbol], List[Symbol]) = - filtered(clazz.info.nonPrivateMembersAdmitting(VBRIDGE).reverseIterator)(m => m.isDeferred & !ignoreDeferred(m))(m => m.isAbstractOverride && m.isIncompleteIn(clazz)) - if (missing.nonEmpty) emitErrors(missing) + filtered(nonPrivateMembers.reverseIterator)(m => m.isDeferred & !ignoreDeferred(m))(m => m.isAbstractOverride && m.isIncompleteIn(clazz)) + if (missing.nonEmpty) emitErrors(missing, nonPrivateMembers) // Check the remainder for invalid absoverride. for (member <- abstractIncomplete) { val explanation = member.superSymbolIn(clazz) match { diff --git a/test/files/neg/t12691.check b/test/files/neg/t12691.check new file mode 100644 index 000000000000..5367c6238725 --- /dev/null +++ b/test/files/neg/t12691.check @@ -0,0 +1,110 @@ +t12691.scala:4: error: class Example needs to be abstract. +Missing implementations for 101 members. + // Members declared in Example + val f1: Int = ??? + val f10: Int = ??? + val f100: Int = ??? + val f11: Int = ??? + val f12: Int = ??? + val f13: Int = ??? + val f14: Int = ??? + val f15: Int = ??? + val f16: Int = ??? + val f17: Int = ??? + val f18: Int = ??? + val f19: Int = ??? + val f2: Int = ??? + val f20: Int = ??? + val f21: Int = ??? + val f22: Int = ??? + val f23: Int = ??? + val f24: Int = ??? + val f25: Int = ??? + val f26: Int = ??? + val f27: Int = ??? + val f28: Int = ??? + val f29: Int = ??? + val f3: Int = ??? + val f30: Int = ??? + val f31: Int = ??? + val f32: Int = ??? + val f33: Int = ??? + val f34: Int = ??? + val f35: Int = ??? + val f36: Int = ??? + val f37: Int = ??? + val f38: Int = ??? + val f39: Int = ??? + val f4: Int = ??? + val f40: Int = ??? + val f41: Int = ??? + val f42: Int = ??? + val f43: Int = ??? + val f44: Int = ??? + val f45: Int = ??? + val f46: Int = ??? + val f47: Int = ??? + val f48: Int = ??? + val f49: Int = ??? + val f5: Int = ??? + val f50: Int = ??? + val f51: Int = ??? + val f52: Int = ??? + val f53: Int = ??? + val f54: Int = ??? + val f55: Int = ??? + val f56: Int = ??? + val f57: Int = ??? + val f58: Int = ??? + val f59: Int = ??? + val f6: Int = ??? + val f60: Int = ??? + val f61: Int = ??? + val f62: Int = ??? + val f63: Int = ??? + val f64: Int = ??? + val f65: Int = ??? + val f66: Int = ??? + val f67: Int = ??? + val f68: Int = ??? + val f69: Int = ??? + val f7: Int = ??? + val f70: Int = ??? + val f71: Int = ??? + val f72: Int = ??? + val f73: Int = ??? + val f74: Int = ??? + val f75: Int = ??? + val f76: Int = ??? + val f77: Int = ??? + val f78: Int = ??? + val f79: Int = ??? + val f8: Int = ??? + val f80: Int = ??? + val f81: Int = ??? + val f82: Int = ??? + val f83: Int = ??? + val f84: Int = ??? + val f85: Int = ??? + val f86: Int = ??? + val f87: Int = ??? + val f88: Int = ??? + val f89: Int = ??? + val f9: Int = ??? + val f90: Int = ??? + val f91: Int = ??? + val f92: Int = ??? + val f93: Int = ??? + val f94: Int = ??? + val f95: Int = ??? + val f96: Int = ??? + val f97: Int = ??? + val f98: Int = ??? + val f99: Int = ??? + + // Members declared in Runner + def run(i: Int): Unit = ??? + +class Example extends Runner { + ^ +1 error diff --git a/test/files/neg/t12691.scala b/test/files/neg/t12691.scala new file mode 100644 index 000000000000..0047f7ae404b --- /dev/null +++ b/test/files/neg/t12691.scala @@ -0,0 +1,106 @@ +abstract class Runner { + def run(i: Int): Unit +} +class Example extends Runner { + def run(s: String) = () + val f1: Int + val f2: Int + val f3: Int + val f4: Int + val f5: Int + val f6: Int + val f7: Int + val f8: Int + val f9: Int + val f10: Int + val f11: Int + val f12: Int + val f13: Int + val f14: Int + val f15: Int + val f16: Int + val f17: Int + val f18: Int + val f19: Int + val f20: Int + val f21: Int + val f22: Int + val f23: Int + val f24: Int + val f25: Int + val f26: Int + val f27: Int + val f28: Int + val f29: Int + val f30: Int + val f31: Int + val f32: Int + val f33: Int + val f34: Int + val f35: Int + val f36: Int + val f37: Int + val f38: Int + val f39: Int + val f40: Int + val f41: Int + val f42: Int + val f43: Int + val f44: Int + val f45: Int + val f46: Int + val f47: Int + val f48: Int + val f49: Int + val f50: Int + val f51: Int + val f52: Int + val f53: Int + val f54: Int + val f55: Int + val f56: Int + val f57: Int + val f58: Int + val f59: Int + val f60: Int + val f61: Int + val f62: Int + val f63: Int + val f64: Int + val f65: Int + val f66: Int + val f67: Int + val f68: Int + val f69: Int + val f70: Int + val f71: Int + val f72: Int + val f73: Int + val f74: Int + val f75: Int + val f76: Int + val f77: Int + val f78: Int + val f79: Int + val f80: Int + val f81: Int + val f82: Int + val f83: Int + val f84: Int + val f85: Int + val f86: Int + val f87: Int + val f88: Int + val f89: Int + val f90: Int + val f91: Int + val f92: Int + val f93: Int + val f94: Int + val f95: Int + val f96: Int + val f97: Int + val f98: Int + val f99: Int + val f100: Int +} diff --git a/test/files/neg/t12691b.check b/test/files/neg/t12691b.check new file mode 100644 index 000000000000..14f2c613a5f6 --- /dev/null +++ b/test/files/neg/t12691b.check @@ -0,0 +1,109 @@ +t12691b.scala:4: error: class Example needs to be abstract. +Missing implementations for 100 members. + // Members declared in Example + val f1: Int = ??? + val f10: Int = ??? + val f11: Int = ??? + val f12: Int = ??? + val f13: Int = ??? + val f14: Int = ??? + val f15: Int = ??? + val f16: Int = ??? + val f17: Int = ??? + val f18: Int = ??? + val f19: Int = ??? + val f2: Int = ??? + val f20: Int = ??? + val f21: Int = ??? + val f22: Int = ??? + val f23: Int = ??? + val f24: Int = ??? + val f25: Int = ??? + val f26: Int = ??? + val f27: Int = ??? + val f28: Int = ??? + val f29: Int = ??? + val f3: Int = ??? + val f30: Int = ??? + val f31: Int = ??? + val f32: Int = ??? + val f33: Int = ??? + val f34: Int = ??? + val f35: Int = ??? + val f36: Int = ??? + val f37: Int = ??? + val f38: Int = ??? + val f39: Int = ??? + val f4: Int = ??? + val f40: Int = ??? + val f41: Int = ??? + val f42: Int = ??? + val f43: Int = ??? + val f44: Int = ??? + val f45: Int = ??? + val f46: Int = ??? + val f47: Int = ??? + val f48: Int = ??? + val f49: Int = ??? + val f5: Int = ??? + val f50: Int = ??? + val f51: Int = ??? + val f52: Int = ??? + val f53: Int = ??? + val f54: Int = ??? + val f55: Int = ??? + val f56: Int = ??? + val f57: Int = ??? + val f58: Int = ??? + val f59: Int = ??? + val f6: Int = ??? + val f60: Int = ??? + val f61: Int = ??? + val f62: Int = ??? + val f63: Int = ??? + val f64: Int = ??? + val f65: Int = ??? + val f66: Int = ??? + val f67: Int = ??? + val f68: Int = ??? + val f69: Int = ??? + val f7: Int = ??? + val f70: Int = ??? + val f71: Int = ??? + val f72: Int = ??? + val f73: Int = ??? + val f74: Int = ??? + val f75: Int = ??? + val f76: Int = ??? + val f77: Int = ??? + val f78: Int = ??? + val f79: Int = ??? + val f8: Int = ??? + val f80: Int = ??? + val f81: Int = ??? + val f82: Int = ??? + val f83: Int = ??? + val f84: Int = ??? + val f85: Int = ??? + val f86: Int = ??? + val f87: Int = ??? + val f88: Int = ??? + val f89: Int = ??? + val f9: Int = ??? + val f90: Int = ??? + val f91: Int = ??? + val f92: Int = ??? + val f93: Int = ??? + val f94: Int = ??? + val f95: Int = ??? + val f96: Int = ??? + val f97: Int = ??? + val f98: Int = ??? + val f99: Int = ??? + + // Members declared in Runner + def run(i: Int): Unit = ??? // Int does not match String in `def run(s: String): Unit` + +class Example extends Runner { + ^ +1 error diff --git a/test/files/neg/t12691b.scala b/test/files/neg/t12691b.scala new file mode 100644 index 000000000000..571936ec2a35 --- /dev/null +++ b/test/files/neg/t12691b.scala @@ -0,0 +1,105 @@ +abstract class Runner { + def run(i: Int): Unit +} +class Example extends Runner { + def run(s: String) = () + val f1: Int + val f2: Int + val f3: Int + val f4: Int + val f5: Int + val f6: Int + val f7: Int + val f8: Int + val f9: Int + val f10: Int + val f11: Int + val f12: Int + val f13: Int + val f14: Int + val f15: Int + val f16: Int + val f17: Int + val f18: Int + val f19: Int + val f20: Int + val f21: Int + val f22: Int + val f23: Int + val f24: Int + val f25: Int + val f26: Int + val f27: Int + val f28: Int + val f29: Int + val f30: Int + val f31: Int + val f32: Int + val f33: Int + val f34: Int + val f35: Int + val f36: Int + val f37: Int + val f38: Int + val f39: Int + val f40: Int + val f41: Int + val f42: Int + val f43: Int + val f44: Int + val f45: Int + val f46: Int + val f47: Int + val f48: Int + val f49: Int + val f50: Int + val f51: Int + val f52: Int + val f53: Int + val f54: Int + val f55: Int + val f56: Int + val f57: Int + val f58: Int + val f59: Int + val f60: Int + val f61: Int + val f62: Int + val f63: Int + val f64: Int + val f65: Int + val f66: Int + val f67: Int + val f68: Int + val f69: Int + val f70: Int + val f71: Int + val f72: Int + val f73: Int + val f74: Int + val f75: Int + val f76: Int + val f77: Int + val f78: Int + val f79: Int + val f80: Int + val f81: Int + val f82: Int + val f83: Int + val f84: Int + val f85: Int + val f86: Int + val f87: Int + val f88: Int + val f89: Int + val f90: Int + val f91: Int + val f92: Int + val f93: Int + val f94: Int + val f95: Int + val f96: Int + val f97: Int + val f98: Int + val f99: Int +} From 568d80a09c80a7430e8714f91743deb3cb773585 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sun, 20 Nov 2022 19:44:44 +0000 Subject: [PATCH 067/261] Update sbt-header to 5.9.0 in 2.12.x --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 89ad07e209a6..d38cbaf10ebe 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -30,6 +30,6 @@ concurrentRestrictions in Global := Seq( Tags.limitAll(1) // workaround for https://github.com/sbt/sbt/issues/2970 ) -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.8.0") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") From bfde2d9fe0f9a6d7c60b871fc0b87cd4f4660893 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sun, 20 Nov 2022 19:48:22 +0000 Subject: [PATCH 068/261] Update sbt to 1.8.0 in 2.12.x --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 6a9f0388941f..8b9a0b0ab037 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.3 +sbt.version=1.8.0 From 83c9994932381dd2fae6b784aa83865b9b177d6f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 17 Nov 2022 13:09:20 -0800 Subject: [PATCH 069/261] Backport fallback to paramless during overload The ancient code comment says it used to work that way. It works that way in Dotty. What's old is new. --- .../tools/nsc/typechecker/ContextErrors.scala | 6 +-- .../tools/nsc/typechecker/Contexts.scala | 4 +- .../scala/tools/nsc/typechecker/Infer.scala | 44 +++++++++++++------ test/files/neg/dbldef.check | 5 +-- test/files/neg/i10715a.check | 38 ++++++++++++++++ test/files/neg/i10715a.scala | 27 ++++++++++++ test/files/neg/i10715b.check | 7 +++ test/files/neg/i10715b.scala | 13 ++++++ test/files/neg/names-defaults-neg-pu.check | 6 ++- test/files/neg/names-defaults-neg-pu.scala | 4 ++ test/files/neg/t3909.check | 3 +- test/files/neg/t3909b.check | 4 ++ test/files/neg/t3909b.scala | 10 +++++ test/files/neg/t4727.check | 5 --- test/files/neg/t4727.scala | 7 --- test/files/neg/t5735.check | 2 +- test/files/pos/i10715a.scala | 31 +++++++++++++ test/files/pos/i10715b/C_1.java | 16 +++++++ test/files/pos/i10715b/caller_2.scala | 15 +++++++ test/files/pos/i6705.scala | 15 +++++++ 20 files changed, 222 insertions(+), 40 deletions(-) create mode 100644 test/files/neg/i10715a.check create mode 100644 test/files/neg/i10715a.scala create mode 100644 test/files/neg/i10715b.check create mode 100644 test/files/neg/i10715b.scala create mode 100644 test/files/neg/t3909b.check create mode 100644 test/files/neg/t3909b.scala delete mode 100644 test/files/neg/t4727.check delete mode 100644 test/files/neg/t4727.scala create mode 100644 test/files/pos/i10715a.scala create mode 100644 test/files/pos/i10715b/C_1.java create mode 100644 test/files/pos/i10715b/caller_2.scala create mode 100644 test/files/pos/i6705.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2521ce0b4c40..251c41cd311f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -107,11 +107,9 @@ trait ContextErrors extends splain.SplainErrors { def issueTypeError(err: AbsTypeError)(implicit context: Context): Unit = { context.issue(err) } + // OPT: avoid error string creation for errors that won't see the light of day def typeErrorMsg(context: Context, found: Type, req: Type) = - if (context.openImplicits.nonEmpty && !settings.Vimplicits.value) - // OPT: avoid error string creation for errors that won't see the light of day, but predicate - // this on -Xsource:2.13 for bug compatibility with https://github.com/scala/scala/pull/7147#issuecomment-418233611 - "type mismatch" + if (!context.openImplicits.isEmpty && !settings.Vimplicits.value) "type mismatch" else "type mismatch" + foundReqMsg(found, req) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 4c4445771cfe..ba06feb4c2e3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -590,7 +590,7 @@ trait Contexts { self: Analyzer => inSilentMode { try { set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode - tryOnce(false) + tryOnce(isLastTry = false) reporter.hasErrors } catch { case ex: CyclicReference => throw ex @@ -602,7 +602,7 @@ trait Contexts { self: Analyzer => // do last try if try with implicits enabled failed // (or if it was not attempted because they were disabled) if (doLastTry) - tryOnce(true) + tryOnce(isLastTry = true) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 7c08012aad1c..c6121e25cd8e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1383,12 +1383,14 @@ trait Infer extends Checkable { * matches prototype `pt`, if it exists. * If several alternatives match `pt`, take parameterless one. * If no alternative matches `pt`, take the parameterless one anyway. + * (There may be more than one parameterless alternative, in particular, + * badly overloaded default args or case class elements. These are detected elsewhere.) */ def inferExprAlternative(tree: Tree, pt: Type): Tree = { val c = context class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice { def tryOnce(isSecondTry: Boolean): Unit = { - val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt)) + val alts0 = alts.filter(alt => isWeaklyCompatible(pre.memberType(alt), pt)) val alts1 = if (alts0.isEmpty) alts else alts0 val bests = bestAlternatives(alts1) { (sym1, sym2) => val tp1 = memberTypeForSpecificity(pre, sym1, tree) @@ -1399,22 +1401,35 @@ trait Infer extends Checkable { || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2) ) } - // todo: missing test case for bests.isEmpty + def finish(s: Symbol): Unit = tree.setSymbol(s).setType(pre.memberType(s)) + def paramlessOr(error: => Unit): Unit = { + val paramless = + if (isSecondTry) + alts.find { alt => val ps = alt.info.paramss; ps.isEmpty || ps.tail.isEmpty && ps.head.isEmpty } + else None + paramless match { + case Some(alt) => finish(alt) + case None => error + } + } bests match { - case best :: Nil => tree setSymbol best setType (pre memberType best) + case best :: Nil => + finish(best) case best :: competing :: _ if alts0.nonEmpty => - // scala/bug#6912 Don't give up and leave an OverloadedType on the tree. - // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try - // unless an error is issued. We're not issuing an error, in the assumption that it would be - // spurious in light of the erroneous expected type - if (pt.isErroneous) setError(tree) - else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) - case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry) + // If erroneous expected type, don't issue spurious error and don't `tryTwice` again with implicits. + // scala/bug#6912 except it does not loop + paramlessOr { + if (pt.isErroneous) setError(tree) + else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry) + } + case _ if bests.isEmpty || alts0.isEmpty => + paramlessOr(NoBestExprAlternativeError(tree, pt, isSecondTry)) + case _ => } } } tree.tpe match { - case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree + case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply(); tree case _ => tree } } @@ -1512,11 +1527,12 @@ trait Infer extends Checkable { val applicable = overloadsToConsiderBySpecificity(alts filter isAltApplicable(pt), argtpes, varargsStar) // println(s"bestForExpectedType($argtpes, $pt): $alts -app-> ${alts filter isAltApplicable(pt)} -arity-> $applicable") val ranked = bestAlternatives(applicable)(rankAlternatives) + def finish(s: Symbol): Unit = tree.setSymbol(s).setType(pre.memberType(s)) ranked match { case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous - case best :: Nil => tree setSymbol best setType (pre memberType best) // success - case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed - case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType + case best :: nil => finish(best) + case nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed + case nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType } } diff --git a/test/files/neg/dbldef.check b/test/files/neg/dbldef.check index 0f7b02a479f8..30b825a73489 100644 --- a/test/files/neg/dbldef.check +++ b/test/files/neg/dbldef.check @@ -6,7 +6,4 @@ dbldef.scala:1: error: type mismatch; required: Int case class test0(x: Int, x: Float) ^ -dbldef.scala:1: error: in class test0, multiple overloaded alternatives of x define default arguments -case class test0(x: Int, x: Float) - ^ -3 errors +2 errors diff --git a/test/files/neg/i10715a.check b/test/files/neg/i10715a.check new file mode 100644 index 000000000000..ca65c021b764 --- /dev/null +++ b/test/files/neg/i10715a.check @@ -0,0 +1,38 @@ +i10715a.scala:16: error: type mismatch; + found : Int + required: String + c.f: String // error + ^ +i10715a.scala:17: error: polymorphic expression cannot be instantiated to expected type; + found : [A]Int + required: String + c.g: String // error + ^ +i10715a.scala:18: error: value bad is not a member of Int + c.f.bad // error + ^ +i10715a.scala:19: error: value bad is not a member of Int + c.g.bad // error + ^ +i10715a.scala:21: error: type mismatch; + found : String("") + required: Int + c.f("") // error + ^ +i10715a.scala:22: error: type mismatch; + found : String("") + required: Int + c.g("") // error + ^ +i10715a.scala:23: error: overloaded method g with alternatives: + (x: Int)Parent + Int + cannot be applied to (String) + c.g[Int]("") // error + ^ +i10715a.scala:24: error: type mismatch; + found : Int + required: String => String + c.g[Int]: (String => String) // error + ^ +8 errors diff --git a/test/files/neg/i10715a.scala b/test/files/neg/i10715a.scala new file mode 100644 index 000000000000..14bcc5a3a4cc --- /dev/null +++ b/test/files/neg/i10715a.scala @@ -0,0 +1,27 @@ +class Parent { + def f(x: Int): Parent = ??? + def f: Int = 0 + + def g[A](x: Int): Parent = ??? + def g[A]: Int = 0 +} + +class Sub extends Parent { + override def f(x: Int): Parent = ??? + override def g[A](x: Int): Parent = ??? +} + +class C { + def bad(c: Sub): Unit = { + c.f: String // error + c.g: String // error + c.f.bad // error + c.g.bad // error + + c.f("") // error + c.g("") // error + c.g[Int]("") // error + c.g[Int]: (String => String) // error + c.g[Int]: (Int => Parent) // ok + } +} diff --git a/test/files/neg/i10715b.check b/test/files/neg/i10715b.check new file mode 100644 index 000000000000..577d8e421fd0 --- /dev/null +++ b/test/files/neg/i10715b.check @@ -0,0 +1,7 @@ +i10715b.scala:12: error: ambiguous reference to overloaded definition, +both method f in class Sub of type (x: Int)(implicit s: String): Unit +and method f in class Sub of type (x: Int): Unit +match argument types (Int) + def bad(c: Sub): Unit = c.f(1) // error: ambiguous overload + ^ +1 error diff --git a/test/files/neg/i10715b.scala b/test/files/neg/i10715b.scala new file mode 100644 index 000000000000..8cc8076ac193 --- /dev/null +++ b/test/files/neg/i10715b.scala @@ -0,0 +1,13 @@ +class Parent { + def f(x: Int): Unit = () + def f: Int = 0 +} + +class Sub extends Parent { + override def f(x: Int): Unit = () + def f(x: Int)(implicit s: String): Unit = () +} + +class C { + def bad(c: Sub): Unit = c.f(1) // error: ambiguous overload +} diff --git a/test/files/neg/names-defaults-neg-pu.check b/test/files/neg/names-defaults-neg-pu.check index dc2216a39317..b4b5d13b1e46 100644 --- a/test/files/neg/names-defaults-neg-pu.check +++ b/test/files/neg/names-defaults-neg-pu.check @@ -127,6 +127,10 @@ names-defaults-neg-pu.scala:141: error: parameter 'a' is already specified at pa names-defaults-neg-pu.scala:142: error: missing parameter type for expanded function (() => b = x$4) val taf4: (Int, String) => Unit = testAnnFun(_, b = _) ^ +names-defaults-neg-pu.scala:193: error: an expression of type Null is ineligible for implicit conversion +Error occurred in an application involving default arguments. + def f = new A3[Int]() + ^ names-defaults-neg-pu.scala:95: warning: the parameter name y is deprecated: use b instead deprNam3(y = 10, b = 2) ^ @@ -137,4 +141,4 @@ names-defaults-neg-pu.scala:100: warning: naming parameter deprNam5Arg is deprec deprNam5(deprNam5Arg = null) ^ 3 warnings -33 errors +34 errors diff --git a/test/files/neg/names-defaults-neg-pu.scala b/test/files/neg/names-defaults-neg-pu.scala index 4fb1a872315c..e381e1119e11 100644 --- a/test/files/neg/names-defaults-neg-pu.scala +++ b/test/files/neg/names-defaults-neg-pu.scala @@ -188,3 +188,7 @@ object t3685 { class u20 { val x: Int = u.f(x = "32") } class u21 { var x: Int = u.f(x = "32") } } +class A3[T](x: T = null) // scala/bug#4727 cf A2 +class t4727 { + def f = new A3[Int]() +} diff --git a/test/files/neg/t3909.check b/test/files/neg/t3909.check index b4d1c26ed14b..9ed443d6b494 100644 --- a/test/files/neg/t3909.check +++ b/test/files/neg/t3909.check @@ -1,5 +1,4 @@ -t3909.scala:1: error: in object DO, multiple overloaded alternatives of m1 define default arguments -Error occurred in an application involving default arguments. +t3909.scala:1: error: in object DO, multiple overloaded alternatives of method m1 define default arguments. object DO { ^ 1 error diff --git a/test/files/neg/t3909b.check b/test/files/neg/t3909b.check new file mode 100644 index 000000000000..7fd44088d4b3 --- /dev/null +++ b/test/files/neg/t3909b.check @@ -0,0 +1,4 @@ +t3909b.scala:1: error: in object DO, multiple overloaded alternatives of method m1 define default arguments. +object DO { + ^ +1 error diff --git a/test/files/neg/t3909b.scala b/test/files/neg/t3909b.scala new file mode 100644 index 000000000000..26875f61c072 --- /dev/null +++ b/test/files/neg/t3909b.scala @@ -0,0 +1,10 @@ +object DO { + + def m1(str: String, extraStuff: String = "stuff"): Int = ??? + def m1(i: Int, extraStuff: Int = "42".toInt): Int = ??? + + def main(args: Array[String]): Unit = { + val m1s = m1("foo") + val m1i = m1(42) + } +} diff --git a/test/files/neg/t4727.check b/test/files/neg/t4727.check deleted file mode 100644 index 3b1c0feac98f..000000000000 --- a/test/files/neg/t4727.check +++ /dev/null @@ -1,5 +0,0 @@ -t4727.scala:5: error: an expression of type Null is ineligible for implicit conversion -Error occurred in an application involving default arguments. - new C[Int] - ^ -1 error diff --git a/test/files/neg/t4727.scala b/test/files/neg/t4727.scala deleted file mode 100644 index 40c06713caa6..000000000000 --- a/test/files/neg/t4727.scala +++ /dev/null @@ -1,7 +0,0 @@ -class C[T](x : T = null) - -object Test { - def main(args: Array[String]): Unit = { - new C[Int] - } -} diff --git a/test/files/neg/t5735.check b/test/files/neg/t5735.check index 8f49838cd6d4..f187d54caadf 100644 --- a/test/files/neg/t5735.check +++ b/test/files/neg/t5735.check @@ -1,5 +1,5 @@ t5735.scala:6: error: type mismatch; - found : (x: Int): Int String + found : String required: Int val z: Int = a ^ diff --git a/test/files/pos/i10715a.scala b/test/files/pos/i10715a.scala new file mode 100644 index 000000000000..db1b243b2c78 --- /dev/null +++ b/test/files/pos/i10715a.scala @@ -0,0 +1,31 @@ +class Parent { + def f(x: Int): Parent = ??? + def f: Int = 0 + + def g[A](x: Int): Parent = ??? + def g[A]: Int = 0 +} + +// For the issue to show up, there must be a subclass that overrides +// one of the two methods. +class Sub extends Parent { + override def f(x: Int): Parent = ??? + override def g[A](x: Int): Parent = ??? +} + +class C { + def test(c: Sub): Unit = { + c.f(1) // already worked + c.f + c.f.+(0) + c.f.toString + + c.g(0) // already worked + c.g + c.g[Int] + c.g.+(0) + c.g.toString + c.g[Int].+(0) + c.g.toString + } +} diff --git a/test/files/pos/i10715b/C_1.java b/test/files/pos/i10715b/C_1.java new file mode 100644 index 000000000000..8973027a0c3c --- /dev/null +++ b/test/files/pos/i10715b/C_1.java @@ -0,0 +1,16 @@ +class C_1 { + + public int f() { + return 0; + } + public C_1 f(int x) { + return null; + } +} + +class Child extends C_1 { + @Override + public C_1 f(int x) { + return null; + } +} diff --git a/test/files/pos/i10715b/caller_2.scala b/test/files/pos/i10715b/caller_2.scala new file mode 100644 index 000000000000..141d606bf3c9 --- /dev/null +++ b/test/files/pos/i10715b/caller_2.scala @@ -0,0 +1,15 @@ +class C { + def test(c: Child): Unit = { + c.f() // always ok + c.f // should work too + c.f(1) + c.f.toString + } + + // The issue was first detected on NIO buffers, + // (on Java 11+), so these should pass now. + def buffer(c: java.nio.ByteBuffer): Unit = { + c.position + c.position(10).position.toString + } +} diff --git a/test/files/pos/i6705.scala b/test/files/pos/i6705.scala new file mode 100644 index 000000000000..6f0d0327b6d2 --- /dev/null +++ b/test/files/pos/i6705.scala @@ -0,0 +1,15 @@ +trait StringTempl { + def mkString: String + def mkString(x: String): String +} + +object Test { + implicit class extension(val x: String) { + def shouldBe(y: String): Boolean = ??? + } + + def test(tmpl: StringTempl): Unit = { + tmpl.mkString shouldBe "hello" // error + tmpl.mkString(", world") shouldBe "hello, world" + } +} From af419008ad663dd07b808226f18f7ccbb348bf3e Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 22 Nov 2022 15:59:21 -0800 Subject: [PATCH 070/261] restore a bit of spec language that went missing the language in question was added by Adriaan in #7680 but then accidentally got removed in 2020 by #7432 I looked through other spec commits in that date span and didn't find anything else that went missing --- spec/06-expressions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/06-expressions.md b/spec/06-expressions.md index f574d7ad2469..1c623a53cf9c 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -1526,7 +1526,9 @@ question: given - A parameterized method ´m´ of type `(´p_1:T_1, \ldots , p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) - to arguments `(´p_1 , \ldots , p_n´)` of types ´T_1 , \ldots , T_n´. + to arguments `(´p_1 , \ldots , p_n´)` of types ´T_1 , \ldots , T_last´; + if ´T_n´ denotes a repeated parameter (it has shape ´T*´), and so does ´m'´'s last parameter, + ´T_last´ is taken as ´T´, otherwise ´T_n´ is used directly. - A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1 , \ldots , n´ each ´a_i´ is an abstract type name From 2aaf0972296f6220b10c29e2971905eebefc695e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 28 Aug 2022 12:42:44 -0700 Subject: [PATCH 071/261] Warn if implicit should have explicit type --- .../tools/nsc/typechecker/ContextErrors.scala | 31 ++++++- .../scala/tools/nsc/typechecker/Namers.scala | 88 +++++++++---------- .../scala/tools/nsc/typechecker/Typers.scala | 23 +++-- .../reflect/internal/StdAttachments.scala | 3 + .../reflect/runtime/JavaUniverseForce.scala | 1 + test/files/neg/t5265a.check | 18 ++++ test/files/neg/t5265a.scala | 32 +++++++ test/files/neg/t5265b.check | 4 + test/files/neg/t5265b.scala | 22 +++++ test/files/neg/t7212.check | 16 ++++ test/files/neg/t7212.scala | 22 +++++ 11 files changed, 200 insertions(+), 60 deletions(-) create mode 100644 test/files/neg/t5265a.check create mode 100644 test/files/neg/t5265a.scala create mode 100644 test/files/neg/t5265b.check create mode 100644 test/files/neg/t5265b.scala create mode 100644 test/files/neg/t7212.check create mode 100644 test/files/neg/t7212.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2521ce0b4c40..0b56ec5495b7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -21,6 +21,7 @@ import scala.annotation.tailrec import scala.reflect.runtime.ReflectionUtils import scala.reflect.macros.runtime.AbortMacroException import scala.util.control.{ControlThrowable, NonFatal} +import scala.tools.nsc.Reporting.WarningCategory import scala.tools.nsc.util.stackTraceString import scala.reflect.io.NoAbstractFile import scala.reflect.internal.util.NoSourceFile @@ -153,7 +154,7 @@ trait ContextErrors extends splain.SplainErrors { MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) /** The implicit not found message from the annotation, and whether it's a supplement message or not. */ - def NoImplicitFoundAnnotation(tree: Tree, param: Symbol): (Boolean, String) = { + def NoImplicitFoundAnnotation(tree: Tree, param: Symbol): (Boolean, String) = param match { case ImplicitNotFoundMsg(msg) => (false, msg.formatParameterMessage(tree)) case _ => @@ -167,7 +168,6 @@ trait ContextErrors extends splain.SplainErrors { true -> supplement } } - } def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = { val (isSupplement, annotationMsg) = NoImplicitFoundAnnotation(tree, param) @@ -186,6 +186,24 @@ trait ContextErrors extends splain.SplainErrors { issueNormalTypeError(tree, if (errMsg.isEmpty) defaultErrMsg else errMsg) } + private def InferredImplicitErrorImpl(tree: Tree, inferred: Type, cx: Context, isTyper: Boolean): Unit = { + def err(): Unit = { + val msg = + s"Implicit definition ${if (currentRun.isScala3) "must" else "should"} have explicit type${ + if (!inferred.isErroneous) s" (inferred $inferred)" else "" + }" + if (currentRun.isScala3) ErrorUtils.issueNormalTypeError(tree, msg)(cx) + else cx.warning(tree.pos, msg, WarningCategory.Other) + } + val sym = tree.symbol + // Defer warning field of class until typing getter (which is marked implicit) + if (sym.isImplicit) { + if (!sym.isLocalToBlock) err() + } + else if (!isTyper && sym.isField && !sym.isLocalToBlock) + sym.updateAttachment(FieldTypeInferred) + } + trait TyperContextErrors { self: Typer => @@ -1049,6 +1067,8 @@ trait ContextErrors extends splain.SplainErrors { def MacroAnnotationTopLevelModuleBadExpansion(ann: Tree) = issueNormalTypeError(ann, "top-level object can only expand into an eponymous object") + def InferredImplicitError(tree: Tree, inferred: Type, cx: Context): Unit = + InferredImplicitErrorImpl(tree, inferred, cx, isTyper = true) } /** This file will be the death of me. */ @@ -1077,7 +1097,7 @@ trait ContextErrors extends splain.SplainErrors { object InferErrorGen { - implicit val contextInferErrorGen = getContext + implicit val contextInferErrorGen: Context = getContext object PolyAlternativeErrorKind extends Enumeration { type ErrorType = Value @@ -1282,7 +1302,7 @@ trait ContextErrors extends splain.SplainErrors { object NamerErrorGen { - implicit val contextNamerErrorGen = context + implicit val contextNamerErrorGen: Context = context object SymValidateErrors extends Enumeration { val ImplicitConstr, ImplicitNotTermOrClass, ImplicitAtToplevel, @@ -1400,6 +1420,9 @@ trait ContextErrors extends splain.SplainErrors { issueNormalTypeError(tree, name.decode + " " + msg) } } + + def InferredImplicitError(tree: Tree, inferred: Type, cx: Context): Unit = + InferredImplicitErrorImpl(tree, inferred, cx, isTyper = false) } trait ImplicitsContextErrors { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 3926a2265b24..ad1aee3579c4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -18,6 +18,7 @@ import scala.collection.mutable import symtab.Flags._ import scala.reflect.internal.util.ListOfNil import scala.tools.nsc.Reporting.WarningCategory +import scala.util.chaining._ /** This trait declares methods to create symbols and to enter them into scopes. * @@ -1149,6 +1150,7 @@ trait Namers extends MethodSynthesis { } if (inferOverridden) pt else dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt)) + .tap(InferredImplicitError(tree, _, context)) }.setPos(tree.pos.focus) tree.tpt.tpe } @@ -1180,9 +1182,7 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner - val parentTrees = typer.typedParentTypes(templ) - val pending = mutable.ListBuffer[AbsTypeError]() parentTrees foreach { tpt => val ptpe = tpt.tpe @@ -1400,7 +1400,6 @@ trait Namers extends MethodSynthesis { * * If the result type is missing, assign a MethodType to `meth` that's constructed using this return type. * This allows omitting the result type for recursive methods. - * */ val resTpFromOverride = if (!inferResTp || overridden == NoSymbol || overridden.isOverloaded) resTpGiven @@ -1459,7 +1458,7 @@ trait Namers extends MethodSynthesis { val resTp = { // When return type is inferred, we don't just use resTpFromOverride -- it must be packed and widened. - // Here, C.f has type String: + // Here, C.f has type String (unless -Xsource:3): // trait T { def f: Object }; class C extends T { def f = "" } // using resTpFromOverride as expected type allows for the following (C.f has type A): // trait T { def f: A }; class C extends T { implicit def b2a(t: B): A = ???; def f = new B } @@ -1721,63 +1720,58 @@ trait Namers extends MethodSynthesis { } } - private def valDefSig(vdef: ValDef) = { + private def valDefSig(vdef: ValDef): Type = { val ValDef(_, _, tpt, rhs) = vdef - val result = - if (tpt.isEmpty) { - if (rhs.isEmpty) { - MissingParameterOrValTypeError(tpt) - ErrorType - } else { - // enterGetterSetter assigns the getter's symbol to a ValDef when there's no underlying field - // (a deferred val or most vals defined in a trait -- see Field.noFieldFor) - val isGetter = vdef.symbol hasFlag ACCESSOR - - val pt = { - val valOwner = owner.owner - // there's no overriding outside of classes, and we didn't use to do this in 2.11, so provide opt-out - - if (!valOwner.isClass) WildcardType - else { - // normalize to getter so that we correctly consider a val overriding a def - // (a val's name ends in a " ", so can't compare to def) - val overridingSym = if (isGetter) vdef.symbol else vdef.symbol.getterIn(valOwner) + def inferredValTpt: Type = { + // enterGetterSetter assigns the getter's symbol to a ValDef when there's no underlying field + // (a deferred val or most vals defined in a trait -- see Field.noFieldFor) + val isGetter = vdef.symbol hasFlag ACCESSOR + + val pt: Type = { + val valOwner = owner.owner + if (!valOwner.isClass) WildcardType + else { + // normalize to getter so that we correctly consider a val overriding a def + // (a val's name ends in a " ", so can't compare to def) + val overridingSym = if (isGetter) vdef.symbol else vdef.symbol.getterIn(valOwner) - // We're called from an accessorTypeCompleter, which is completing the info for the accessor's symbol, - // which may or may not be `vdef.symbol` (see isGetter above) - val overridden = safeNextOverriddenSymbol(overridingSym) + // We're called from an accessorTypeCompleter, which is completing the info for the accessor's symbol, + // which may or may not be `vdef.symbol` (see isGetter above) + val overridden = safeNextOverriddenSymbol(overridingSym) - if (overridden == NoSymbol || overridden.isOverloaded) WildcardType - else valOwner.thisType.memberType(overridden).resultType - } - } + if (overridden == NoSymbol || overridden.isOverloaded) WildcardType + else valOwner.thisType.memberType(overridden).resultType + } + } - def patchSymInfo(tp: Type): Unit = - if (pt ne WildcardType) // no patching up to do if we didn't infer a prototype - vdef.symbol setInfo (if (isGetter) NullaryMethodType(tp) else tp) + def patchSymInfo(tp: Type): Unit = + if (pt ne WildcardType) // no patching up to do if we didn't infer a prototype + vdef.symbol.setInfo { if (isGetter) NullaryMethodType(tp) else tp } - patchSymInfo(pt) + patchSymInfo(pt) - // derives the val's result type from type checking its rhs under the expected type `pt` - // vdef.tpt is mutated, and `vdef.tpt.tpe` is `assignTypeToTree`'s result - val tptFromRhsUnderPt = assignTypeToTree(vdef, typer, pt) + // derives the val's result type from type checking its rhs under the expected type `pt` + // vdef.tpt is mutated, and `vdef.tpt.tpe` is `assignTypeToTree`'s result + val tptFromRhsUnderPt = assignTypeToTree(vdef, typer, pt) - // need to re-align with assignTypeToTree, as the type we're returning from valDefSig (tptFromRhsUnderPt) - // may actually go to the accessor, not the valdef (and if assignTypeToTree returns a subtype of `pt`, - // we would be out of synch between field and its accessors), and thus the type completer won't - // fix the symbol's info for us -- we set it to tmpInfo above, which may need to be improved to tptFromRhsUnderPt - if (!isGetter) patchSymInfo(tptFromRhsUnderPt) + // need to re-align with assignTypeToTree, as the type we're returning from valDefSig (tptFromRhsUnderPt) + // may actually go to the accessor, not the valdef (and if assignTypeToTree returns a subtype of `pt`, + // we would be out of synch between field and its accessors), and thus the type completer won't + // fix the symbol's info for us -- we set it to tmpInfo above, which may need to be improved to tptFromRhsUnderPt + if (!isGetter) patchSymInfo(tptFromRhsUnderPt) - tptFromRhsUnderPt - } + tptFromRhsUnderPt + } + val result: Type = + if (tpt.isEmpty) { + if (rhs.isEmpty) { MissingParameterOrValTypeError(tpt); ErrorType } + else inferredValTpt } else { val tptTyped = typer.typedType(tpt) context.unit.transformed(tpt) = tptTyped tptTyped.tpe } - // println(s"val: $result / ${vdef.tpt.tpe} / ") - pluginsTypeSig(result, typer, vdef, if (tpt.isEmpty) WildcardType else result) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index aaf4eabfc9f1..f76643fafcb4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2173,7 +2173,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } /** Analyze the super constructor call to record information used later to compute parameter aliases */ - def analyzeSuperConsructor(meth: Symbol, vparamss: List[List[ValDef]], rhs: Tree): Unit = { + def analyzeSuperConstructor(meth: Symbol, vparamss: List[List[ValDef]], rhs: Tree): Unit = { val clazz = meth.owner debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs") val pending = ListBuffer[AbsTypeError]() @@ -2355,7 +2355,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - val tparams1 = ddef.tparams mapConserve typedTypeDef + val tparams1 = ddef.tparams.mapConserve(typedTypeDef) val vparamss1 = ddef.vparamss.mapConserve(_.mapConserve(typedValDef)) warnTypeParameterShadow(tparams1, meth) @@ -2395,9 +2395,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (meth.isClassConstructor && !isPastTyper && !meth.owner.isSubClass(AnyValClass) && !meth.isJava) { // There are no supercalls for AnyVal or constructors from Java sources, which - // would blow up in analyzeSuperConsructor; there's nothing to be computed for them anyway. + // would blow up in analyzeSuperConstructor; there's nothing to be computed for them anyway. if (meth.isPrimaryConstructor) - analyzeSuperConsructor(meth, vparamss1, rhs1) + analyzeSuperConstructor(meth, vparamss1, rhs1) else checkSelfConstructorArgs(ddef, meth.owner) } @@ -2424,13 +2424,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(ddef) - if (meth.isImplicit && !meth.isSynthetic) meth.paramss match { - case List(param) :: _ if !param.isImplicit => - checkFeature(ddef.pos, currentRun.runDefinitions.ImplicitConversionsFeature, meth.toString) - case _ => + if (meth.isImplicit) { + if (!meth.isSynthetic) meth.paramss match { + case List(param) :: _ if !param.isImplicit => + checkFeature(ddef.pos, currentRun.runDefinitions.ImplicitConversionsFeature, meth.toString) + case _ => + } + if (meth.isGetter && !meth.isLocalToBlock && meth.accessed.hasAttachment[FieldTypeInferred.type]) { + meth.accessed.removeAttachment[FieldTypeInferred.type] + InferredImplicitError(ddef, meth.accessed.tpe.resultType, context) + } } } - treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } finally { currentRun.profiler.afterTypedImplDef(meth) diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 6045c42adec3..2aefc327cca2 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -149,4 +149,7 @@ trait StdAttachments { /** Marks a Typed tree with Unit tpt. */ case object TypedExpectingUnitAttachment def explicitlyUnit(tree: Tree): Boolean = tree.hasAttachment[TypedExpectingUnitAttachment.type] + + /** For `val i = 42`, marks field as inferred so accessor (getter) can warn if implicit. */ + case object FieldTypeInferred extends PlainAttachment } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index ead2054e9092..b4d932c591d9 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -78,6 +78,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.InterpolatedString this.RootSelection this.TypedExpectingUnitAttachment + this.FieldTypeInferred this.noPrint this.typeDebug // inaccessible: this.posAssigner diff --git a/test/files/neg/t5265a.check b/test/files/neg/t5265a.check new file mode 100644 index 000000000000..f788868ec4b0 --- /dev/null +++ b/test/files/neg/t5265a.check @@ -0,0 +1,18 @@ +t5265a.scala:7: warning: Implicit definition should have explicit type (inferred T[String]) + implicit val tsMissing = new T[String] {} // warn val in trait + ^ +t5265a.scala:20: warning: Implicit definition should have explicit type (inferred T[String]) + implicit val tsChild = new T[String] {} // warn because inferred from RHS + ^ +t5265a.scala:22: warning: Implicit definition should have explicit type (inferred Int) + implicit private[this] val pChild = 42 // also warn + ^ +t5265a.scala:27: warning: Implicit definition should have explicit type (inferred Int) + implicit private[this] val y = 42 // also warn + ^ +t5265a.scala:25: warning: Implicit definition should have explicit type (inferred T[String]) + implicit val tsD = new T[String] {} // warn val in class + ^ +error: No warnings can be incurred under -Werror. +5 warnings +1 error diff --git a/test/files/neg/t5265a.scala b/test/files/neg/t5265a.scala new file mode 100644 index 000000000000..80b127e1adeb --- /dev/null +++ b/test/files/neg/t5265a.scala @@ -0,0 +1,32 @@ +// scalac: -Werror +trait T[A] + +class C[A: T] + +trait Missing { + implicit val tsMissing = new T[String] {} // warn val in trait + def f = new C[String] +} +trait Local { + def f = { + implicit val tsLocal = new T[String] {} // nowarn because local + new C[String] + } +} +trait Parent { + def t: T[String] +} +trait Child extends Parent { + implicit val tsChild = new T[String] {} // warn because inferred from RHS + def f = new C[String] + implicit private[this] val pChild = 42 // also warn +} +class D { + implicit val tsD = new T[String] {} // warn val in class + def f = new C[String] + implicit private[this] val y = 42 // also warn +} +class X extends Missing +trait Z { + val z = 42 +} diff --git a/test/files/neg/t5265b.check b/test/files/neg/t5265b.check new file mode 100644 index 000000000000..97f96fd82be1 --- /dev/null +++ b/test/files/neg/t5265b.check @@ -0,0 +1,4 @@ +t5265b.scala:7: error: Implicit definition must have explicit type (inferred T[String]) + implicit val tsMissing = new T[String] {} // warn val in trait + ^ +1 error diff --git a/test/files/neg/t5265b.scala b/test/files/neg/t5265b.scala new file mode 100644 index 000000000000..19512c3c9180 --- /dev/null +++ b/test/files/neg/t5265b.scala @@ -0,0 +1,22 @@ +// scalac: -Werror -Xlint -Xsource:3 +trait T[A] + +class C[A: T] + +trait Missing { + implicit val tsMissing = new T[String] {} // warn val in trait + def f = new C[String] +} +trait Local { + def f = { + implicit val tsLocal = new T[String] {} // nowarn because local + new C[String] + } +} +trait Parent { + def tsChild: T[String] +} +trait Child extends Parent { + implicit val tsChild = new T[String] {} // nowarn because inferred from overridden + def f = new C[String] +} diff --git a/test/files/neg/t7212.check b/test/files/neg/t7212.check new file mode 100644 index 000000000000..8f1fe20edcb4 --- /dev/null +++ b/test/files/neg/t7212.check @@ -0,0 +1,16 @@ +t7212.scala:8: error: type mismatch; + found : Object + required: String + val s: String = k.f + ^ +t7212.scala:14: error: type mismatch; + found : Object + required: String + val s: String = f.f + ^ +t7212.scala:21: error: type mismatch; + found : Object + required: String + val s: String = w.f + ^ +3 errors diff --git a/test/files/neg/t7212.scala b/test/files/neg/t7212.scala new file mode 100644 index 000000000000..5dd16a6c8091 --- /dev/null +++ b/test/files/neg/t7212.scala @@ -0,0 +1,22 @@ + +// scalac: -Xsource:3 + +trait T { def f: Object } +class K extends T { def f = "" } +object K { + val k = new K + val s: String = k.f +} + +class F extends T { val f = "" } +object F { + val f = new F + val s: String = f.f +} + +trait V extends T { var f = "" } +class W extends V +object W { + val w = new W + val s: String = w.f +} From 22531888cc67ef4418d63623a9808f65a539024d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 28 Aug 2022 12:47:40 -0700 Subject: [PATCH 072/261] Tweak internals --- src/compiler/scala/tools/nsc/PipelineMain.scala | 2 +- .../scala/tools/nsc/typechecker/AnalyzerPlugins.scala | 3 ++- src/manual/scala/tools/docutil/ManPage.scala | 6 +++--- src/partest/scala/tools/partest/CompilerTest.scala | 3 ++- src/partest/scala/tools/partest/nest/Instance.scala | 2 +- src/partest/scala/tools/partest/nest/Reference.scala | 2 +- src/partest/scala/tools/partest/package.scala | 3 +-- src/reflect/scala/reflect/api/Internals.scala | 2 +- src/reflect/scala/reflect/internal/Trees.scala | 10 ++++------ .../scala/tools/nsc/interpreter/MemberHandlers.scala | 2 +- src/repl/scala/tools/nsc/interpreter/Power.scala | 4 ++-- src/repl/scala/tools/nsc/interpreter/ReplVals.scala | 8 ++++++-- src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala | 2 +- .../tools/scalap/scalax/rules/scalasig/ScalaSig.scala | 5 +++-- 14 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/compiler/scala/tools/nsc/PipelineMain.scala b/src/compiler/scala/tools/nsc/PipelineMain.scala index 06adc05f8cc6..ccc151454594 100644 --- a/src/compiler/scala/tools/nsc/PipelineMain.scala +++ b/src/compiler/scala/tools/nsc/PipelineMain.scala @@ -77,7 +77,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe } } - implicit val executor = ExecutionContext.fromExecutor(new java.util.concurrent.ForkJoinPool(parallelism), t => handler.uncaughtException(Thread.currentThread(), t)) + implicit val executor: ExecutionContext = ExecutionContext.fromExecutor(new java.util.concurrent.ForkJoinPool(parallelism), t => handler.uncaughtException(Thread.currentThread(), t)) def changeExtension(p: Path, newExtension: String): Path = { val fileName = p.getFileName.toString val changedFileName = fileName.lastIndexOf('.') match { diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index 2557867ea966..9fd8343ea7ce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -54,7 +54,8 @@ trait AnalyzerPlugins { self: Analyzer with splain.SplainData => /** * Let analyzer plugins change the types assigned to definitions. For definitions that have * an annotated type, the assigned type is obtained by typing that type tree. Otherwise, the - * type is inferred by typing the definition's righthand side. + * type is inferred by typing the definition's righthand side, or from the overridden + * member under `-Xsource:3`. * * In order to know if the type was inferred, you can query the `wasEmpty` field in the `tpt` * TypeTree of the definition (for DefDef and ValDef). diff --git a/src/manual/scala/tools/docutil/ManPage.scala b/src/manual/scala/tools/docutil/ManPage.scala index 853c17b94c0b..cfd9844629f2 100644 --- a/src/manual/scala/tools/docutil/ManPage.scala +++ b/src/manual/scala/tools/docutil/ManPage.scala @@ -24,7 +24,7 @@ object ManPage { case class Emph(contents: AbstractText) extends AbstractText case class Mono(contents: AbstractText) extends AbstractText case class Quote(contents: AbstractText) extends AbstractText - implicit def str2text(str: String) = Text(str) + implicit def str2text(str: String): Text = Text(str) case class Definition(term: AbstractText, description: AbstractText) case class DefinitionList(definitions: Definition*) extends AbstractText @@ -37,14 +37,14 @@ object ManPage { case class CodeSample(text: String) extends Paragraph case class BlockQuote(text: AbstractText) extends Paragraph implicit def text2para(text: AbstractText): Paragraph = TextParagraph(text) - implicit def str2para(str: String) = text2para(str2text(str)) + implicit def str2para(str: String): Paragraph = text2para(str2text(str)) case class BulletList(items: AbstractText*) extends Paragraph case class NumberedList(items: AbstractText*) extends Paragraph case class TitledPara(title: String, text: AbstractText) extends Paragraph case class EmbeddedSection(section: Section) extends Paragraph - implicit def section2Para(section: Section) = EmbeddedSection(section) + implicit def section2Para(section: Section): EmbeddedSection = EmbeddedSection(section) case class Section(title: String, paragraphs: Paragraph*) diff --git a/src/partest/scala/tools/partest/CompilerTest.scala b/src/partest/scala/tools/partest/CompilerTest.scala index f45846848d83..8114d071c460 100644 --- a/src/partest/scala/tools/partest/CompilerTest.scala +++ b/src/partest/scala/tools/partest/CompilerTest.scala @@ -12,6 +12,7 @@ package scala.tools.partest +import scala.language.implicitConversions import scala.reflect.runtime.{universe => ru} import scala.tools.nsc._ @@ -46,7 +47,7 @@ abstract class CompilerTest extends DirectTest { if (sym eq NoSymbol) NoType else appliedType(sym, compilerTypeFromTag(t)) } - implicit def mkMkType(sym: Symbol) = new MkType(sym) + implicit def mkMkType(sym: Symbol): MkType = new MkType(sym) def allMembers(root: Symbol): List[Symbol] = { def loop(seen: Set[Symbol], roots: List[Symbol]): List[Symbol] = { diff --git a/src/partest/scala/tools/partest/nest/Instance.scala b/src/partest/scala/tools/partest/nest/Instance.scala index 7c0827e99a05..21da651e7031 100644 --- a/src/partest/scala/tools/partest/nest/Instance.scala +++ b/src/partest/scala/tools/partest/nest/Instance.scala @@ -28,5 +28,5 @@ trait Instance extends Spec { def residualArgs = parsed.residualArgs // only args which were not options or args to options type OptionMagic = Opt.Instance - protected implicit def optionMagicAdditions(name: String) = new Opt.Instance(programInfo, parsed, name) + protected implicit def optionMagicAdditions(name: String): Opt.Instance = new Opt.Instance(programInfo, parsed, name) } diff --git a/src/partest/scala/tools/partest/nest/Reference.scala b/src/partest/scala/tools/partest/nest/Reference.scala index f068c7d785f6..ecf07319f8a5 100644 --- a/src/partest/scala/tools/partest/nest/Reference.scala +++ b/src/partest/scala/tools/partest/nest/Reference.scala @@ -45,7 +45,7 @@ trait Reference extends Spec { final def apply(args: String*): ThisCommandLine = creator(propertyArgs ++ args flatMap expandArg) type OptionMagic = Opt.Reference - protected implicit def optionMagicAdditions(name: String) = new Opt.Reference(programInfo, options, name) + protected implicit def optionMagicAdditions(name: String): Opt.Reference = new Opt.Reference(programInfo, options, name) } object Reference { diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index 5484b5dc8b94..ccddabe77feb 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -18,6 +18,7 @@ import java.util.concurrent.{Callable, ExecutorService} import scala.concurrent.duration.Duration import scala.io.Codec import scala.jdk.CollectionConverters._ +import scala.language.implicitConversions import scala.tools.nsc.util.Exceptional package object partest { @@ -123,8 +124,6 @@ package object partest { implicit def temporaryPath2File(x: Path): File = x.jfile implicit def stringPathToJavaFile(path: String): File = new File(path) - implicit lazy val implicitConversions = scala.language.implicitConversions - def fileSeparator = java.io.File.separator def pathSeparator = java.io.File.pathSeparator diff --git a/src/reflect/scala/reflect/api/Internals.scala b/src/reflect/scala/reflect/api/Internals.scala index 1478f55f897f..3e2f5f4fe736 100644 --- a/src/reflect/scala/reflect/api/Internals.scala +++ b/src/reflect/scala/reflect/api/Internals.scala @@ -1098,7 +1098,7 @@ trait Internals { self: Universe => trait CompatApi { /** @see [[CompatToken]] */ @deprecated("compatibility with Scala 2.10 EOL", "2.13.0") - implicit val token = new CompatToken + implicit val token: CompatToken = new CompatToken /** Scala 2.10 compatibility enrichments for BuildApi. */ @deprecated("compatibility with Scala 2.10 EOL", "2.13.0") diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 0217da48700c..07b3fbb21226 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1464,15 +1464,13 @@ trait Trees extends api.Trees { override def removeAttachment[T: ClassTag]: this.type = attachmentWarning() private def attachmentWarning(): this.type = {devWarning(s"Attempt to mutate attachments on $self ignored"); this} - private def requireLegal(value: Any, allowed: Any, what: String) = ( - if (value != allowed) { + private def requireLegal(value: Any, allowed: Any, what: String): Unit = + if (value != allowed && this != pendingSuperCall) { log(s"can't set $what for $self to value other than $allowed") if (settings.isDebug && settings.isDeveloper) - (new Throwable).printStackTrace + new Throwable(s"can't set $what for $self to value other than $allowed").printStackTrace } - ) - override def traverse(traverser: Traverser): Unit = - () + override def traverse(traverser: Traverser): Unit = () } case object EmptyTree extends TermTree with CannotHaveAttrs { diff --git a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index 73639213cc6f..5208ae1797a5 100644 --- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -34,7 +34,7 @@ trait MemberHandlers { val front = if (leadingPlus) "+ " else "" front + (xs map string2codeQuoted mkString " + ") } - private implicit def name2string(name: Name) = name.toString + private implicit def name2string(name: Name): String = name.toString /** A traverser that finds all mentioned identifiers, i.e. things * that need to be imported. It might return extra names. diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index bf0996559a2e..0d65f8bcf5f5 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -264,7 +264,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re trait Implicits1 { // fallback - implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = + implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]): PrettifierClass[T] = new SinglePrettifierClass[T](x) } trait Implicits2 extends Implicits1 { @@ -288,7 +288,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re implicit def replPrettifier[T] : Prettifier[T] = Prettifier.default[T] implicit def replTypeApplication(sym: Symbol): RichSymbol = new RichSymbol(sym) - implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) + implicit def replInputStream(in: InputStream)(implicit codec: Codec): RichInputStream = new RichInputStream(in) implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) } diff --git a/src/repl/scala/tools/nsc/interpreter/ReplVals.scala b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala index 2e485d2ac787..1213c99c117f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala @@ -47,13 +47,17 @@ class StdReplVals(final val intp: IMain) extends ReplVals { import intp.global.Symbol private val tagFn = ReplVals.mkCompilerTypeFromTag[intp.global.type](global) - implicit def mkCompilerTypeFromTag(sym: Symbol) = tagFn(sym) + implicit def mkCompilerTypeFromTag(sym: Symbol): ATFT[global.type] = tagFn(sym) } final lazy val replImplicits = new ReplImplicits def typed[T <: analyzer.global.Tree](tree: T): T = typer.typed(tree).asInstanceOf[T] } +trait ATFT[G <: Global] { + def apply[M](implicit m1: ru.TypeTag[M]): G#Type + def apply[M1, M2](implicit m1: ru.TypeTag[M1], m2: ru.TypeTag[M2]): G#Type +} object ReplVals { /** Latest attempt to work around the challenge of foo.global.Type @@ -72,7 +76,7 @@ object ReplVals { def compilerTypeFromTag(t: ApiUniverse # WeakTypeTag[_]): Global#Type = definitions.compilerTypeFromTag(t) - class AppliedTypeFromTags(sym: Symbol) { + class AppliedTypeFromTags(sym: Symbol) extends ATFT[T] { def apply[M](implicit m1: ru.TypeTag[M]): Type = if (sym eq NoSymbol) NoType else appliedType(sym, compilerTypeFromTag(m1).asInstanceOf[Type]) diff --git a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala index df9b5c679ffb..6494a3d49008 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala @@ -29,7 +29,7 @@ trait Uncompilable { import global.definitions.{ AnyRefClass, AnyRefTpe } import global.rootMirror.RootClass - private implicit def translateName(name: Global#Name) = + private implicit def translateName(name: Global#Name): global.Name = if (name.isTypeName) newTypeName("" + name) else newTermName("" + name) val WaitNames = List("scala", "AnyRef", "wait") diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala index e571649454a8..cbc0b53960f5 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSig.scala @@ -114,7 +114,7 @@ case class ScalaSig(majorVersion: Int, minorVersion: Int, table: Seq[Int ~ ByteC def parseEntry(index: Int) = applyRule(ScalaSigParsers.parseEntry(ScalaSigEntryParsers.entry)(index)) - implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(parser)(this) + implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]): A = ScalaSigParsers.expect(parser)(this) override def toString = "ScalaSig version " + majorVersion + "." + minorVersion + { for (i <- 0 until table.size) yield "" + i + ":\t" + parseEntry(i) // + "\n\t" + getEntry(i) @@ -165,7 +165,8 @@ object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { def parseEntry[A](parser: EntryParser[A])(index: Int) = (toEntry(index) -~ parser) - implicit def entryType(code: Int) = key filter (_ == code) + type R = scala.tools.scalap.scalax.rules.Rule[ScalaSigEntryParsers.S, ScalaSigEntryParsers.S, Int, Nothing] + implicit def entryType(code: Int): R = key.filter(_ == code) val index = read(_.index) val key = read(_.entryType) From 1659fdcf0f6e72426c7a6fa8d1ccaedf7dd5ef47 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 28 Aug 2022 13:41:25 -0700 Subject: [PATCH 073/261] Tweak tests for inferred implicits --- test/async/jvm/toughtype.scala | 2 +- test/files/jvm/future-spec/main.scala | 14 ++++-- test/files/jvm/interpreter.check | 2 + test/files/jvm/scala-concurrent-tck.scala | 2 +- test/files/neg/implicit-log.scala | 8 ++-- test/files/neg/implicits.check | 7 +++ test/files/neg/override-final-implicit.check | 4 ++ test/files/neg/private-implicit-class.check | 4 ++ test/files/neg/t1038.scala | 1 + test/files/neg/t2206.check | 4 ++ test/files/neg/t2421b.check | 4 ++ test/files/neg/t2866.scala | 2 +- test/files/neg/t3006.check | 4 ++ test/files/neg/t3346c.check | 2 +- test/files/neg/t3346c.scala | 7 ++- test/files/neg/t3346i.check | 16 +++++++ test/files/neg/t4271.check | 19 ++++++++ test/files/neg/t4457_1.check | 16 +++++++ test/files/neg/t4457_2.check | 16 +++++++ test/files/neg/t4568.check | 4 ++ test/files/neg/t4889.check | 4 ++ test/files/neg/t5728.check | 4 ++ test/files/neg/t6436.check | 7 +++ test/files/neg/t6436b.check | 7 +++ test/files/neg/t6567.check | 5 +- test/files/neg/t6667.check | 4 ++ test/files/neg/t6667.scala | 2 +- test/files/neg/t692.check | 4 ++ test/files/neg/t712.check | 7 +++ test/files/neg/t7131.check | 7 +++ test/files/neg/t729.check | 7 +++ test/files/neg/t8322.check | 7 +++ test/files/pos/t7212.scala | 11 +++++ test/files/run/Meter.scala | 4 +- test/files/run/MeterCaseClass.scala | 4 +- test/files/run/idempotency-lazy-vals.scala | 2 +- test/files/run/impconvtimes.scala | 2 +- test/files/run/implicits.scala | 2 +- .../Macros_Test_2.scala | 2 +- .../run/macro-implicit-decorator/Test_2.scala | 6 +-- test/files/run/reflection-implicit.scala | 2 +- .../repl-class-based-implicit-import.check | 2 +- .../repl-class-based-implicit-import.scala | 2 +- .../run/repl-no-imports-no-predef-power.check | 1 - test/files/run/repl-power.check | 1 - .../files/run/sip23-implicit-resolution.scala | 8 ++-- test/files/run/t2316_run.scala | 4 +- test/files/run/t2514.scala | 5 +- test/files/run/t2866.scala | 2 +- test/files/run/t3346d.scala | 2 +- test/files/run/t3346e.scala | 6 +-- test/files/run/t3346f.scala | 4 +- test/files/run/t3346h.check | 1 - test/files/run/t3346h.scala | 4 +- test/files/run/t3346j.scala | 2 +- test/files/run/t5080.scala | 2 +- test/files/run/t5276_2b.scala | 2 +- test/files/run/t5565.scala | 10 ++++ test/files/run/t6290.scala | 3 +- test/files/run/t6327.scala | 4 +- test/files/run/t657.scala | 4 +- test/files/run/t8564.scala | 4 +- test/files/run/tcpoly_parseridioms.scala | 4 +- test/junit/scala/io/SourceTest.scala | 2 +- .../util/AbstractFileClassLoaderTest.scala | 4 +- test/macro-annot/run/scopes/scopes_2.scala | 2 +- .../collection/mutable/MutableTreeMap.scala | 6 +-- .../collection/mutable/MutableTreeSet.scala | 2 +- .../quasiquotes/ArbitraryTreesAndNames.scala | 4 +- test/scalacheck/treemap.scala | 2 +- test/scaladoc/run/diagrams-base.check | 9 ++++ test/scaladoc/run/diagrams-filtering.check | 6 +++ test/scaladoc/run/implicits-ambiguating.check | 6 +++ test/scaladoc/run/implicits-base.check | 18 +++++++ test/scaladoc/run/implicits-chaining.check | 15 ++++++ .../run/implicits-known-type-classes.check | 48 +++++++++++++++++++ test/scaladoc/run/implicits-shadowing.check | 3 ++ test/scaladoc/run/implicits-var-exp.check | 6 +++ test/tasty/neg/src-2/TestCtxFns_fail.scala | 2 +- 79 files changed, 375 insertions(+), 75 deletions(-) delete mode 100644 test/files/run/t3346h.check diff --git a/test/async/jvm/toughtype.scala b/test/async/jvm/toughtype.scala index 939d0647e985..9587afa35f19 100644 --- a/test/async/jvm/toughtype.scala +++ b/test/async/jvm/toughtype.scala @@ -199,7 +199,7 @@ package scala.async.run.toughtype { } object FunDep { - implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]) = + implicit def `Something to do with List`[W, S, R](implicit funDep: FunDep[W, S, R]): FunDep[W,List[S],W] = new FunDep[W, List[S], W] { def method(w: W, l: List[S]) = async { val it = l.iterator diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index 411a6333992f..dda7cfbb4e47 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -17,9 +17,10 @@ object Test { } trait Features { - implicit def implicitously = scala.language.implicitConversions - implicit def reflectively = scala.language.reflectiveCalls - implicit def postulously = scala.language.postfixOps + import languageFeature._ + implicit def implicitously: implicitConversions = scala.language.implicitConversions + implicit def reflectively: reflectiveCalls = scala.language.reflectiveCalls + implicit def postulously: postfixOps = scala.language.postfixOps } @@ -40,7 +41,9 @@ trait MinimalScalaTest extends Output with Features with Vigil { if (throwables.nonEmpty) println(buffer.toString) } - implicit def stringops(s: String) = new { + type Ops = AnyRef{def should[U](snippets: => U): U; def in[U](snippet: => U): scala.collection.mutable.IndexedSeq[_ >: Char with Throwable] with scala.collection.mutable.AbstractSeq[_ >: Char with Throwable] with scala.collection.mutable.Growable[Char with Throwable] with java.io.Serializable} + + implicit def stringops(s: String): Ops = new { def should[U](snippets: => U) = { bufferPrintln(s + " should:") @@ -62,7 +65,8 @@ trait MinimalScalaTest extends Output with Features with Vigil { } - implicit def objectops(obj: Any) = new { + type OOps = AnyRef{def mustBe(other: Any): Unit; def mustEqual(other: Any): Unit} + implicit def objectops(obj: Any): OOps = new { def mustBe(other: Any) = assert(obj == other, s"$obj is not $other") def mustEqual(other: Any) = mustBe(other) diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index 3acf90e25db0..2e9430f729c4 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -87,6 +87,8 @@ scala> case class Bar(n: Int) class Bar scala> implicit def foo2bar(foo: Foo) = Bar(foo.n) + ^ + warning: Implicit definition should have explicit type (inferred Bar) warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature` def foo2bar(foo: Foo): Bar diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index d8eafde41b16..3633582cb376 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -1055,7 +1055,7 @@ class ExecutionContextPrepare extends TestBase { delegate.reportFailure(t) } - implicit val ec = new PreparingExecutionContext + implicit val ec: ExecutionContext = new PreparingExecutionContext def testOnComplete(): Unit = once { done => diff --git a/test/files/neg/implicit-log.scala b/test/files/neg/implicit-log.scala index f77085e3c2af..7af20610f64d 100644 --- a/test/files/neg/implicit-log.scala +++ b/test/files/neg/implicit-log.scala @@ -37,8 +37,8 @@ object Test1579 { class Query[E](val value: E) class Invoker(q: Any) { val foo = null } - implicit def unwrap[C](q: Query[C]) = q.value - implicit def invoker(q: Query[Column]) = new Invoker(q) + implicit def unwrap[C](q: Query[C]): C = q.value + implicit def invoker(q: Query[Column]): Invoker = new Invoker(q) val q = new Query(new Column) q.foo @@ -50,9 +50,9 @@ object Test1625 { def unwrap() = x } - implicit def byName[A](x: => A) = new Wrapped(x) + implicit def byName[A](x: => A): Wrapped = new Wrapped(x) - implicit def byVal[A](x: A) = x + implicit def byVal[A](x: A): A = x def main(args: Array[String]) = { diff --git a/test/files/neg/implicits.check b/test/files/neg/implicits.check index 2eb03eb5f3db..6bcdf6c81285 100644 --- a/test/files/neg/implicits.check +++ b/test/files/neg/implicits.check @@ -16,4 +16,11 @@ implicits.scala:47: error: type mismatch; implicits.scala:59: error: could not find implicit value for parameter x: Nothing foo { ^ +implicits.scala:34: warning: Implicit definition should have explicit type (inferred T) + implicit def select[T](t: HSome[T,_]) = t.head + ^ +implicits.scala:35: warning: Implicit definition should have explicit type (inferred L) + implicit def selectTail[L](t: HSome[_,L]) = t.tail + ^ +2 warnings 4 errors diff --git a/test/files/neg/override-final-implicit.check b/test/files/neg/override-final-implicit.check index d46efa1acb52..d5e546078794 100644 --- a/test/files/neg/override-final-implicit.check +++ b/test/files/neg/override-final-implicit.check @@ -1,5 +1,9 @@ +override-final-implicit.scala:6: warning: Implicit definition should have explicit type (inferred Test.this.FooExtender) + override implicit def FooExtender(foo: String) = super.FooExtender(foo) + ^ override-final-implicit.scala:6: error: cannot override final member: final implicit def FooExtender(foo: String): Test.this.FooExtender (defined in class Implicits) override implicit def FooExtender(foo: String) = super.FooExtender(foo) ^ +1 warning 1 error diff --git a/test/files/neg/private-implicit-class.check b/test/files/neg/private-implicit-class.check index 31ddc9a023ab..1578f9102325 100644 --- a/test/files/neg/private-implicit-class.check +++ b/test/files/neg/private-implicit-class.check @@ -1,4 +1,8 @@ private-implicit-class.scala:6: error: method BarExtender in class ImplicitsPrivate cannot be accessed as a member of ImplicitsPrivate from class TestPrivate override implicit def BarExtender(bar: Int) = super.BarExtender(bar) // error ^ +private-implicit-class.scala:6: warning: Implicit definition should have explicit type + override implicit def BarExtender(bar: Int) = super.BarExtender(bar) // error + ^ +1 warning 1 error diff --git a/test/files/neg/t1038.scala b/test/files/neg/t1038.scala index 367022965b99..2cc45a190eea 100644 --- a/test/files/neg/t1038.scala +++ b/test/files/neg/t1038.scala @@ -4,5 +4,6 @@ object Y { val a = new X import a._ implicit val b : Int = 1 + @annotation.nowarn implicit val c = 2 } diff --git a/test/files/neg/t2206.check b/test/files/neg/t2206.check index 5ea5570f9a90..75c5e19c58e5 100644 --- a/test/files/neg/t2206.check +++ b/test/files/neg/t2206.check @@ -2,4 +2,8 @@ t2206.scala:10: error: value f is not a member of o.A Note: implicit method ax is not applicable here because it comes after the application point and it lacks an explicit result type a.f() ^ +t2206.scala:13: warning: Implicit definition should have explicit type (inferred o.AX) + implicit def ax(a: A) = new AX + ^ +1 warning 1 error diff --git a/test/files/neg/t2421b.check b/test/files/neg/t2421b.check index 7c714f1c9bd7..644395fc6a12 100644 --- a/test/files/neg/t2421b.check +++ b/test/files/neg/t2421b.check @@ -1,4 +1,8 @@ t2421b.scala:12: error: could not find implicit value for parameter aa: Test.F[Test.A] f ^ +t2421b.scala:10: warning: Implicit definition should have explicit type (inferred Test.F[X]) + implicit def b[X <: B] = new F[X]() + ^ +1 warning 1 error diff --git a/test/files/neg/t2866.scala b/test/files/neg/t2866.scala index 2f464593d30c..d80822349244 100644 --- a/test/files/neg/t2866.scala +++ b/test/files/neg/t2866.scala @@ -1,7 +1,7 @@ // for 2.7.x compatibility object A { - implicit val one = 1 + implicit val one: Int = 1 } object Test { diff --git a/test/files/neg/t3006.check b/test/files/neg/t3006.check index b8dea080a766..e2358b0f0dd9 100644 --- a/test/files/neg/t3006.check +++ b/test/files/neg/t3006.check @@ -3,4 +3,8 @@ t3006.scala:8: error: type mismatch; required: Int println(A(3) + "H") ^ +t3006.scala:6: warning: Implicit definition should have explicit type (inferred Test.Foo) + implicit def aToFoo(x: A) = new Foo(x); + ^ +1 warning 1 error diff --git a/test/files/neg/t3346c.check b/test/files/neg/t3346c.check index 10c96eeb6cdb..4b18680e493d 100644 --- a/test/files/neg/t3346c.check +++ b/test/files/neg/t3346c.check @@ -1,4 +1,4 @@ -t3346c.scala:60: error: value bar is not a member of Either[Int,String] +t3346c.scala:65: error: value bar is not a member of Either[Int,String] did you mean map? eii.bar ^ diff --git a/test/files/neg/t3346c.scala b/test/files/neg/t3346c.scala index 59584e0a6168..287c91bdf2ca 100644 --- a/test/files/neg/t3346c.scala +++ b/test/files/neg/t3346c.scala @@ -1,3 +1,5 @@ +import annotation._ + object Test extends App { // // An attempt to workaround scala/bug#2712, foiled by scala/bug#3346 @@ -32,15 +34,18 @@ object Test extends App { } + @nowarn implicit def ToTCValue[M[_], A](ma: M[A])(implicit M0: TC[M]) = new TCValue[M, A] { - implicit val M = M0 + implicit val M: TC[M] = M0 val self = ma } implicit def ToTCValueBin1[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[A, α]})#λ]): TCValue[({type λ[α] = M[A, α]})#λ, B] = new TCValue[({type λ[α]=M[A, α]})#λ, B] { + @nowarn implicit val M = M0 val self = ma } implicit def ToTCValueBin2[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[α, B]})#λ]): TCValue[({type λ[α]=M[α, B]})#λ, A] = new TCValue[({type λ[α]=M[α, B]})#λ, A] { + @nowarn implicit val M = M0 val self = ma } diff --git a/test/files/neg/t3346i.check b/test/files/neg/t3346i.check index b1b5a140e8f9..dafa0b861e24 100644 --- a/test/files/neg/t3346i.check +++ b/test/files/neg/t3346i.check @@ -4,4 +4,20 @@ t3346i.scala:28: error: value a is not a member of Test.A[T] t3346i.scala:29: error: value a is not a member of Test.A[Nothing] (new A[Nothing]).a ^ +t3346i.scala:16: warning: Implicit definition should have explicit type (inferred Implicit1[T]) + implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b) + ^ +t3346i.scala:18: warning: Implicit definition should have explicit type (inferred Implicit2[T]) + implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +t3346i.scala:19: warning: Implicit definition should have explicit type (inferred Implicit2[T]) + implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +t3346i.scala:21: warning: Implicit definition should have explicit type (inferred Implicit3[T]) + implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]() + ^ +t3346i.scala:22: warning: Implicit definition should have explicit type (inferred Implicit3[T]) + implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]() + ^ +5 warnings 2 errors diff --git a/test/files/neg/t4271.check b/test/files/neg/t4271.check index 402a7b9b7acb..d89d07f420a7 100644 --- a/test/files/neg/t4271.check +++ b/test/files/neg/t4271.check @@ -8,4 +8,23 @@ t4271.scala:11: error: value -> is not a member of Int did you mean >>>? 3 -> 5 ^ +t4271.scala:3: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def Ensuring[A](x: A) = Donotuseme + ^ +t4271.scala:4: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def doubleWrapper(x: Int) = Donotuseme + ^ +t4271.scala:5: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def floatWrapper(x: Int) = Donotuseme + ^ +t4271.scala:6: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def intWrapper(x: Int) = Donotuseme + ^ +t4271.scala:7: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def longWrapper(x: Int) = Donotuseme + ^ +t4271.scala:8: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) + implicit def ArrowAssoc[A](x: A) = Donotuseme + ^ +6 warnings 3 errors diff --git a/test/files/neg/t4457_1.check b/test/files/neg/t4457_1.check index a16bb1ff8b1b..4d65bc39c1e0 100644 --- a/test/files/neg/t4457_1.check +++ b/test/files/neg/t4457_1.check @@ -4,4 +4,20 @@ and method aFunc in object ImplicitConvAmbiguity2 of type [A](a: ImplicitConvAm match argument types (Float) val x = aFunc(4F) ^ +t4457_1.scala:11: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NE[Float]) + implicit def conv1(i: Float) = new NE[Float] + ^ +t4457_1.scala:12: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.TooManyListenersException]) + implicit def conv3(op: AA[java.util.TooManyListenersException]) = new N[java.util.TooManyListenersException] + ^ +t4457_1.scala:13: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[Float]) + implicit def conv4(op: AA[Float]) = new N[Float] + ^ +t4457_1.scala:14: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NZ[Float]) + implicit def conv7(i: Float) = new NZ[Float] + ^ +t4457_1.scala:15: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.GregorianCalendar]) + implicit def conv5(e: BB[java.util.GregorianCalendar]) = new N[java.util.GregorianCalendar] + ^ +5 warnings 1 error diff --git a/test/files/neg/t4457_2.check b/test/files/neg/t4457_2.check index b1310f74ed50..d0be6d48ef38 100644 --- a/test/files/neg/t4457_2.check +++ b/test/files/neg/t4457_2.check @@ -10,4 +10,20 @@ and method aFunc in object ImplicitConvAmbiguity2 of type [A](a: ImplicitConvAm match argument types (Float) bFunc(aFunc(4F)) ^ +t4457_2.scala:11: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NE[Float]) + implicit def conv1(i: Float) = new NE[Float] + ^ +t4457_2.scala:12: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.TooManyListenersException]) + implicit def conv3(op: AA[java.util.TooManyListenersException]) = new N[java.util.TooManyListenersException] + ^ +t4457_2.scala:13: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[Float]) + implicit def conv4(op: AA[Float]) = new N[Float] + ^ +t4457_2.scala:14: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.NZ[Float]) + implicit def conv7(i: Float) = new NZ[Float] + ^ +t4457_2.scala:15: warning: Implicit definition should have explicit type (inferred ImplicitConvAmbiguity2.N[java.util.GregorianCalendar]) + implicit def conv5(e: BB[java.util.GregorianCalendar]) = new N[java.util.GregorianCalendar] + ^ +5 warnings 2 errors diff --git a/test/files/neg/t4568.check b/test/files/neg/t4568.check index 3582e24b188a..604948e41b47 100644 --- a/test/files/neg/t4568.check +++ b/test/files/neg/t4568.check @@ -1,4 +1,8 @@ t4568.scala:8: error: recursive method isSubListOf needs result type case h :: t => y.contains(h) && (t.isSubListOf(y.drop(y.indexOf(h) + 1))) ^ +t4568.scala:2: warning: Implicit definition should have explicit type (inferred SubList.SubListable[A]) + implicit def sublistable[A](x: List[A]) = new SubListable(x) + ^ +1 warning 1 error diff --git a/test/files/neg/t4889.check b/test/files/neg/t4889.check index 96e9b7528e67..e643dd1ffc05 100644 --- a/test/files/neg/t4889.check +++ b/test/files/neg/t4889.check @@ -1,4 +1,8 @@ t4889.scala:19: error: could not find implicit value for parameter ma1: t4889.MatrixAdder[Int,[S]t4889.SparseMatrix[S]] m1.foo ^ +t4889.scala:14: warning: Implicit definition should have explicit type (inferred t4889.MatrixAdder[S,R]) + implicit def adderImplicit[S, R[s] <: Matrix[s, R]] = new MatrixAdder[S, R] { + ^ +1 warning 1 error diff --git a/test/files/neg/t5728.check b/test/files/neg/t5728.check index 7a80af2af06d..72a0c0273005 100644 --- a/test/files/neg/t5728.check +++ b/test/files/neg/t5728.check @@ -1,4 +1,8 @@ t5728.scala:3: error: implicit classes must accept exactly one primary constructor parameter implicit class Foo ^ +t5728.scala:5: warning: Implicit definition should have explicit type (inferred Test.Foo) + implicit def Foo = new Foo + ^ +1 warning 1 error diff --git a/test/files/neg/t6436.check b/test/files/neg/t6436.check index 27f4466572e5..2d74aa8c2a78 100644 --- a/test/files/neg/t6436.check +++ b/test/files/neg/t6436.check @@ -7,4 +7,11 @@ Note that implicit conversions are not applicable because they are ambiguous: are possible conversion functions from StringContext to ?{def q: ?} println(q"a") ^ +t6436.scala:2: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo1(ctx: StringContext) = new { def q = ??? } + ^ +t6436.scala:3: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo2(ctx: StringContext) = new { def q = ??? } + ^ +2 warnings 1 error diff --git a/test/files/neg/t6436b.check b/test/files/neg/t6436b.check index be9b17a4e387..8e33d7bb134a 100644 --- a/test/files/neg/t6436b.check +++ b/test/files/neg/t6436b.check @@ -7,4 +7,11 @@ Note that implicit conversions are not applicable because they are ambiguous: are possible conversion functions from StringContext to ?{def q: ?} println(StringContext("a").q()) ^ +t6436b.scala:2: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo1(ctx: StringContext) = new { def q = ??? } + ^ +t6436b.scala:3: warning: Implicit definition should have explicit type (inferred AnyRef{def q: Nothing}) + implicit def foo2(ctx: StringContext) = new { def q = ??? } + ^ +2 warnings 1 error diff --git a/test/files/neg/t6567.check b/test/files/neg/t6567.check index 5ae946a4c1ac..552f4cbd48df 100644 --- a/test/files/neg/t6567.check +++ b/test/files/neg/t6567.check @@ -1,3 +1,6 @@ +t6567.scala:8: warning: Implicit definition should have explicit type (inferred B) + implicit def a2b(a: A) = new B + ^ t6567.scala:10: warning: Suspicious application of an implicit view (Test.this.a2b) in the argument to Option.apply. Option[B](a) ^ @@ -6,5 +9,5 @@ t6567.scala:12: warning: Suspicious application of an implicit view (Test.this.a ^ warning: 1 feature warning; re-run with -feature for details error: No warnings can be incurred under -Werror. -3 warnings +4 warnings 1 error diff --git a/test/files/neg/t6667.check b/test/files/neg/t6667.check index 1fe4a15e593e..dfcad24c4645 100644 --- a/test/files/neg/t6667.check +++ b/test/files/neg/t6667.check @@ -10,4 +10,8 @@ t6667.scala:9: error: ambiguous implicit values: match expected type C implicitly[C] // ambiguity reported, rather than falling back to C.companion ^ +t6667.scala:3: warning: Implicit definition should have explicit type (inferred C) + implicit def companion = new C + ^ +1 warning 2 errors diff --git a/test/files/neg/t6667.scala b/test/files/neg/t6667.scala index fb857ebd3322..840921f3449d 100644 --- a/test/files/neg/t6667.scala +++ b/test/files/neg/t6667.scala @@ -4,7 +4,7 @@ object C { } object Test { - implicit val inScope1, inScope2 = new C + implicit val inScope1, inScope2: C = new C implicitly[C]: Unit // C.companion was used; whereas the ambiguity should abort the implicit search. implicitly[C] // ambiguity reported, rather than falling back to C.companion } diff --git a/test/files/neg/t692.check b/test/files/neg/t692.check index 670495f6467d..42e2a1b8f2c6 100644 --- a/test/files/neg/t692.check +++ b/test/files/neg/t692.check @@ -16,4 +16,8 @@ t692.scala:14: error: class Foo takes type parameters t692.scala:19: error: class Foo takes type parameters class Bar[A <: Foo](implicit tpeA : Type[A]) extends Foo; ^ +t692.scala:11: warning: Implicit definition should have explicit type (inferred test3.this.FooType) + implicit def typeOfFoo = FooType(); + ^ +1 warning 6 errors diff --git a/test/files/neg/t712.check b/test/files/neg/t712.check index fc8f7824f2c7..606e41f04d92 100644 --- a/test/files/neg/t712.check +++ b/test/files/neg/t712.check @@ -1,4 +1,11 @@ t712.scala:10: error: overloaded method coerce needs result type implicit def coerce(p : ParentImpl) = p.self; ^ +t712.scala:3: warning: Implicit definition should have explicit type (inferred A.this.Node) + implicit def coerce(n : NodeImpl) = n.self; + ^ +t712.scala:10: warning: Implicit definition should have explicit type + implicit def coerce(p : ParentImpl) = p.self; + ^ +2 warnings 1 error diff --git a/test/files/neg/t7131.check b/test/files/neg/t7131.check index c66f1b4f927c..86df78172b9c 100644 --- a/test/files/neg/t7131.check +++ b/test/files/neg/t7131.check @@ -4,4 +4,11 @@ t7131.scala:21: error: type mismatch; Note: implicit method convertToSimpleMappable is not applicable here because it comes after the application point and it lacks an explicit result type x.value.map(f) ^ +t7131.scala:28: warning: Implicit definition should have explicit type (inferred ObservableValue.TraversableMappable[T,Container]) + implicit def convertToTraversableMappable[T, Container[X] <: Traversable[X]](x: ObservableValue[Container[T]]) = + ^ +t7131.scala:43: warning: Implicit definition should have explicit type (inferred ObservableValue.NestedMappable[T,Container]) + implicit def convertToSimpleMappable[T, Container[X] <: ObservableValue.HasMap[X, Container]](x: ObservableValue[Container[T]]) = + ^ +2 warnings 1 error diff --git a/test/files/neg/t729.check b/test/files/neg/t729.check index 050772cf84b9..7680b2fdfa4b 100644 --- a/test/files/neg/t729.check +++ b/test/files/neg/t729.check @@ -3,4 +3,11 @@ t729.scala:20: error: type mismatch; required: ScalaParserAutoEdit.this.NodeImpl(in trait ScalaParserAutoEdit) val yyy : NodeImpl = link.from; ^ +t729.scala:3: warning: Implicit definition should have explicit type (inferred Parser.this.Node) + implicit def coerce(n : NodeImpl) = n.self; + ^ +t729.scala:14: warning: Implicit definition should have explicit type (inferred ScalaParserAutoEdit.this.Node) + implicit def coerce(node : NodeImpl) = node.self; + ^ +2 warnings 1 error diff --git a/test/files/neg/t8322.check b/test/files/neg/t8322.check index 6c3f9a5e53ce..bc56230cd385 100644 --- a/test/files/neg/t8322.check +++ b/test/files/neg/t8322.check @@ -13,4 +13,11 @@ t8322.scala:19: error: type mismatch; required: scala.util.Either[?,?] Right(0).right.flatMap(_ => new String()) ^ +t8322.scala:15: warning: Implicit definition should have explicit type (inferred Writes[Seq[E]]) + implicit def rw[E] = Writes[Seq[E]] { _ => "" } + ^ +t8322.scala:17: warning: Implicit definition should have explicit type + implicit def wr[E] = jw(implicitly, implicitly) + ^ +2 warnings 3 errors diff --git a/test/files/pos/t7212.scala b/test/files/pos/t7212.scala index 1c1a3f07a2c2..4b53c034ff1f 100644 --- a/test/files/pos/t7212.scala +++ b/test/files/pos/t7212.scala @@ -13,3 +13,14 @@ class C extends B { override def f: Option[String] = Some("goodbye, cruel world") override def remove(): Unit = println("removed! (not really)") } + +trait T { def f: Object } +class K extends T { def f = "" } +object K { + val k = new K + val s: Any = k.f +} + +trait U extends T { def f = "" } +trait V { var v: Any } +trait W extends V { var v = "" } diff --git a/test/files/run/Meter.scala b/test/files/run/Meter.scala index 9fa8a7babd7d..7ef9d247e1ba 100644 --- a/test/files/run/Meter.scala +++ b/test/files/run/Meter.scala @@ -23,7 +23,7 @@ package a { def apply(x: Double): Meter = new Meter(x) - implicit val boxings = new BoxingConversions[Meter, Double] { + implicit val boxings: BoxingConversions[Meter, Double] = new BoxingConversions[Meter, Double] { def box(x: Double) = new Meter(x) def unbox(m: Meter) = m.underlying } @@ -35,7 +35,7 @@ package a { override def toString = unbox.toString+"ft" } object Foot { - implicit val boxings = new BoxingConversions[Foot, Double] { + implicit val boxings: BoxingConversions[Foot, Double] = new BoxingConversions[Foot, Double] { def box(x: Double) = new Foot(x) def unbox(m: Foot) = m.unbox } diff --git a/test/files/run/MeterCaseClass.scala b/test/files/run/MeterCaseClass.scala index 8c15bbb6d30b..1f40dc5e7dbc 100644 --- a/test/files/run/MeterCaseClass.scala +++ b/test/files/run/MeterCaseClass.scala @@ -20,7 +20,7 @@ package a { private[a] trait MeterArg - implicit val boxings = new BoxingConversions[Meter, Double] { + implicit val boxings: BoxingConversions[Meter, Double] = new BoxingConversions[Meter, Double] { def box(x: Double) = new Meter(x) def unbox(m: Meter) = m.underlying } @@ -32,7 +32,7 @@ package a { override def toString = unbox.toString+"ft" } object Foot { - implicit val boxings = new BoxingConversions[Foot, Double] { + implicit val boxings: BoxingConversions[Foot, Double] = new BoxingConversions[Foot, Double] { def box(x: Double) = new Foot(x) def unbox(m: Foot) = m.unbox } diff --git a/test/files/run/idempotency-lazy-vals.scala b/test/files/run/idempotency-lazy-vals.scala index 8688add3e699..236e1516137c 100644 --- a/test/files/run/idempotency-lazy-vals.scala +++ b/test/files/run/idempotency-lazy-vals.scala @@ -7,7 +7,7 @@ object Test extends App { val lazee = reify { class C { lazy val x = 2 - implicit lazy val y = 3 + implicit lazy val y: Int = 3 } val c = new C() import c._ diff --git a/test/files/run/impconvtimes.scala b/test/files/run/impconvtimes.scala index 477a16a8903d..d377685a9c77 100644 --- a/test/files/run/impconvtimes.scala +++ b/test/files/run/impconvtimes.scala @@ -9,7 +9,7 @@ object Test { def *(newUnit: Unit) = Measure(scalar, newUnit) } - implicit def double2Measure(scalar: Double) = + implicit def double2Measure(scalar: Double): Measure = Measure(scalar, NoUnit) diff --git a/test/files/run/implicits.scala b/test/files/run/implicits.scala index 5681a9d484dc..fbc8536c041b 100644 --- a/test/files/run/implicits.scala +++ b/test/files/run/implicits.scala @@ -2,7 +2,7 @@ import scala.language.implicitConversions object A { object B { - implicit def int2string(x: Int) = "["+x.toString+"]" + implicit def int2string(x: Int): String = "["+x.toString+"]" } } diff --git a/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala b/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala index 46367cd1a307..a126bd813d46 100644 --- a/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala +++ b/test/files/run/macro-expand-implicit-macro-has-implicit/Macros_Test_2.scala @@ -1,6 +1,6 @@ import scala.language.experimental.macros object Test extends App { - implicit val x = 42 + implicit val x: Int = 42 def foo(implicit x: Int): Unit = macro Impls.foo foo } diff --git a/test/files/run/macro-implicit-decorator/Test_2.scala b/test/files/run/macro-implicit-decorator/Test_2.scala index bfcb57986997..88c60d91b09f 100644 --- a/test/files/run/macro-implicit-decorator/Test_2.scala +++ b/test/files/run/macro-implicit-decorator/Test_2.scala @@ -5,9 +5,9 @@ class CustomClass trait MyTC[A] object MyTC { - implicit val forInt = new MyTC[Int] {} - implicit def forList[A](implicit a: Derivation[MyTC[A]]) = new MyTC[List[A]] {} - implicit def forCustomClass(implicit a: Derivation[MyTC[List[Boolean]]]) = new MyTC[CustomClass] {} + implicit val forInt: MyTC[Int] = new MyTC[Int] {} + implicit def forList[A](implicit a: Derivation[MyTC[A]]): MyTC[List[A]] = new MyTC[List[A]] {} + implicit def forCustomClass(implicit a: Derivation[MyTC[List[Boolean]]]): MyTC[CustomClass] = new MyTC[CustomClass] {} } object Test extends App { diff --git a/test/files/run/reflection-implicit.scala b/test/files/run/reflection-implicit.scala index a6e939322ad5..1e104d1e5809 100644 --- a/test/files/run/reflection-implicit.scala +++ b/test/files/run/reflection-implicit.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions import scala.reflect.runtime.universe._ class C { - implicit val v = new C + implicit val v: C = new C implicit def d(x: C)(implicit c: C): Int = ??? implicit class X(val x: Int) } diff --git a/test/files/run/repl-class-based-implicit-import.check b/test/files/run/repl-class-based-implicit-import.check index 9ad75cba7540..85d97a78c1fd 100644 --- a/test/files/run/repl-class-based-implicit-import.check +++ b/test/files/run/repl-class-based-implicit-import.check @@ -2,7 +2,7 @@ scala> def showInt(implicit x: Int) = println(x) def showInt(implicit x: Int): Unit -scala> object IntHolder { implicit val myInt = 5 } +scala> object IntHolder { implicit val myInt: Int = 5 } object IntHolder scala> import IntHolder.myInt diff --git a/test/files/run/repl-class-based-implicit-import.scala b/test/files/run/repl-class-based-implicit-import.scala index 9153c8e08f7f..133c99969f66 100644 --- a/test/files/run/repl-class-based-implicit-import.scala +++ b/test/files/run/repl-class-based-implicit-import.scala @@ -11,7 +11,7 @@ object Test extends ReplTest { def code = """ |def showInt(implicit x: Int) = println(x) - |object IntHolder { implicit val myInt = 5 } + |object IntHolder { implicit val myInt: Int = 5 } |import IntHolder.myInt |showInt |class A; showInt diff --git a/test/files/run/repl-no-imports-no-predef-power.check b/test/files/run/repl-no-imports-no-predef-power.check index 64c4b37b92e2..52140e1b5c2c 100644 --- a/test/files/run/repl-no-imports-no-predef-power.check +++ b/test/files/run/repl-no-imports-no-predef-power.check @@ -11,7 +11,6 @@ warning: 1 deprecation (since 2.11.0); for details, enable `:setting -deprecatio val res0: $r.global.noSelfType.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with tags -warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature` val tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index 64c4b37b92e2..52140e1b5c2c 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -11,7 +11,6 @@ warning: 1 deprecation (since 2.11.0); for details, enable `:setting -deprecatio val res0: $r.global.noSelfType.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with tags -warning: 1 feature warning; for details, enable `:setting -feature` or `:replay -feature` val tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/sip23-implicit-resolution.scala b/test/files/run/sip23-implicit-resolution.scala index 5d83f89c43b1..beef7ae73e9f 100644 --- a/test/files/run/sip23-implicit-resolution.scala +++ b/test/files/run/sip23-implicit-resolution.scala @@ -4,11 +4,11 @@ object Test extends App { def mkAssoc[K, V0](k: K, v0: V0): Assoc[k.type] { type V = V0 } = new Assoc[k.type] {type V = V0 ; val v = v0} def lookup[K](k: K)(implicit a: Assoc[k.type]): a.V = a.v - implicit def firstAssoc = mkAssoc(1, "Panda!") - implicit def secondAssoc = mkAssoc(2, "Kitty!") + implicit def firstAssoc: Assoc[1] { type V = String } = mkAssoc(1, "Panda!") + implicit def secondAssoc: Assoc[2] { type V = String } = mkAssoc(2, "Kitty!") - implicit def ageAssoc = mkAssoc("Age", 3) - implicit def nmAssoc = mkAssoc("Name", "Jane") + implicit def ageAssoc: Assoc["Age"] { type V = Int } = mkAssoc("Age", 3) + implicit def nmAssoc: Assoc["Name"] { type V = String } = mkAssoc("Name", "Jane") assert(lookup(1) == "Panda!") assert(lookup(2) == "Kitty!") diff --git a/test/files/run/t2316_run.scala b/test/files/run/t2316_run.scala index 19d326f57f1a..2a17886aea46 100644 --- a/test/files/run/t2316_run.scala +++ b/test/files/run/t2316_run.scala @@ -1,7 +1,7 @@ case class T1(source: String) object T1 { - implicit def T1FromT2(implicit t2: T2) = new T1(t2.source) + implicit def T1FromT2(implicit t2: T2): T1 = new T1(t2.source) } case class T2(source: String) @@ -10,7 +10,7 @@ object A { def requireT1(implicit t1: T1) = t1 object B1 { - implicit val t2_b1 = new T2("from B1") + implicit val t2_b1: T2 = new T2("from B1") requireT1 } diff --git a/test/files/run/t2514.scala b/test/files/run/t2514.scala index 0bf716e8bbf6..6a58c8f63c36 100644 --- a/test/files/run/t2514.scala +++ b/test/files/run/t2514.scala @@ -2,9 +2,8 @@ import scala.language.{ implicitConversions, postfixOps, reflectiveCalls } -object Test -{ - implicit def x[A](a: A) = new { def xx = a } +object Test { + implicit def x[A](a: A): AnyRef{def xx: A} = new { def xx = a } def main(args: Array[String]): Unit = { val r1 = 12 xx; diff --git a/test/files/run/t2866.scala b/test/files/run/t2866.scala index c1583e91ace9..7c65e3c406bd 100644 --- a/test/files/run/t2866.scala +++ b/test/files/run/t2866.scala @@ -1,7 +1,7 @@ // for 2.7.x compatibility object A { - implicit val one = 1 + implicit val one: Int = 1 } object Test extends App { diff --git a/test/files/run/t3346d.scala b/test/files/run/t3346d.scala index 118318dee67d..261028dea3a9 100644 --- a/test/files/run/t3346d.scala +++ b/test/files/run/t3346d.scala @@ -11,7 +11,7 @@ object Test extends App { def create(v: A): Basket[A,B] } - implicit val bf = new BasketFactory[Int,TARInt] { + implicit val bf: BasketFactory[Int,TARInt] = new BasketFactory[Int,TARInt] { def create(v: Int): Basket[Int,TARInt] = new Basket[Int, TARInt]{} } diff --git a/test/files/run/t3346e.scala b/test/files/run/t3346e.scala index 8f75eb97edd3..66bba85818f6 100644 --- a/test/files/run/t3346e.scala +++ b/test/files/run/t3346e.scala @@ -47,9 +47,9 @@ class FilterMapFixed[A, Repr <% IterableOps[A, Iterable, _]](a: Repr) { } object MyEnhancements { - implicit def toQS[Coll](a: Coll) = new QuickSort(a) - implicit def toFM[Coll](a: Coll) = new FilterMap(a) - implicit def toFM2[A, Repr <% IterableOps[A, Iterable, _]](a: Repr) = new FilterMapFixed(a) + implicit def toQS[Coll](a: Coll): QuickSort[Coll] = new QuickSort(a) + implicit def toFM[Coll](a: Coll): FilterMap[Coll] = new FilterMap(a) + implicit def toFM2[A, Repr <% IterableOps[A, Iterable, _]](a: Repr): FilterMapFixed[A,Repr] = new FilterMapFixed(a) } object Test extends App { diff --git a/test/files/run/t3346f.scala b/test/files/run/t3346f.scala index 4799ca2ca9a5..b2dc16de5bfd 100644 --- a/test/files/run/t3346f.scala +++ b/test/files/run/t3346f.scala @@ -4,11 +4,11 @@ import scala.language.reflectiveCalls object Test extends App { trait Foo[A] implicit def fooString: Foo[String] = null - implicit def value[A](implicit foo: Foo[A]) = 5 + implicit def value[A](implicit foo: Foo[A]): Int = 5 println(implicitly[Int]) - implicit def conversion[A](x: Int)(implicit foo: Foo[A]) = new { + implicit def conversion[A](x: Int)(implicit foo: Foo[A]): AnyRef{def aMethod: Int} = new { def aMethod = 5 } println(1.aMethod) diff --git a/test/files/run/t3346h.check b/test/files/run/t3346h.check deleted file mode 100644 index 587be6b4c3f9..000000000000 --- a/test/files/run/t3346h.check +++ /dev/null @@ -1 +0,0 @@ -x diff --git a/test/files/run/t3346h.scala b/test/files/run/t3346h.scala index 642b7cf58505..3e94e25df7ed 100644 --- a/test/files/run/t3346h.scala +++ b/test/files/run/t3346h.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Test extends App { trait Fundep[T, U] { def u(t: T): U } class C { def y = "x" } - implicit val FundepStringC = new Fundep[String, C]{ def u(t: String) = new C } + implicit val FundepStringC: Fundep[String,C] = new Fundep[String, C]{ def u(t: String) = new C } implicit def foo[T, U](x: T)(implicit y: Fundep[T, U]): U = y.u(x) - println("x".y) + assert("x".y == "x") } diff --git a/test/files/run/t3346j.scala b/test/files/run/t3346j.scala index 719d11819af4..bfacb6d0e17a 100644 --- a/test/files/run/t3346j.scala +++ b/test/files/run/t3346j.scala @@ -5,7 +5,7 @@ import scala.reflect.runtime.universe._ object Test extends App { class A[T] class B[T] - implicit def foo[T: TypeTag](a: A[T])(implicit b: B[T]) = new { def baz = typeOf[T] } + implicit def foo[T: TypeTag](a: A[T])(implicit b: B[T]): AnyRef{def baz: reflect.runtime.universe.Type} = new { def baz = typeOf[T] } implicit def bar[T <: Int]: B[T] = new B[T]() println(new A[Int]().baz) } diff --git a/test/files/run/t5080.scala b/test/files/run/t5080.scala index acb6167f4652..8d0d04fd60ec 100644 --- a/test/files/run/t5080.scala +++ b/test/files/run/t5080.scala @@ -11,7 +11,7 @@ object Test extends App { override def toString = value.toString; } - implicit def conversions(x: Value) = new { + implicit def conversions(x: Value): AnyRef { def toInt: Int } = new { def toInt = x match { case Num(n) => n diff --git a/test/files/run/t5276_2b.scala b/test/files/run/t5276_2b.scala index 9cc789ec22c8..eb3d744befa4 100644 --- a/test/files/run/t5276_2b.scala +++ b/test/files/run/t5276_2b.scala @@ -4,7 +4,7 @@ import scala.tools.reflect.Eval object Test extends App { reify { class C { - implicit lazy val x = 2 + implicit lazy val x: Int = 2 def y = implicitly[Int] } diff --git a/test/files/run/t5565.scala b/test/files/run/t5565.scala index 9ced87ca21a7..3c9ed24003af 100644 --- a/test/files/run/t5565.scala +++ b/test/files/run/t5565.scala @@ -1,7 +1,9 @@ +import scala.annotation.nowarn import scala.language.reflectiveCalls import scala.language.implicitConversions object Test extends App { + @nowarn // the inferred type includes the default arg, which can't be written explicitly implicit def doubleWithApproxEquals(d: Double) = new { def ~==(v: Double, margin: Double = 0.001): Boolean = math.abs(d - v) < margin @@ -10,3 +12,11 @@ object Test extends App { assert(math.abs(-4.0) ~== (4.0, 0.001)) assert(math.abs(-4.0) ~== 4.0) } +/* +was: +Exception in thread "main" java.lang.IllegalAccessError: tried to access field illegal_access_error_test_case$.reflParams$Cache2 from class illegal_access_error_test_case$delayedInit$body + at illegal_access_error_test_case$delayedInit$body.(illegal_access_error_test_case.scala:8) + at illegal_access_error_test_case$.(illegal_access_error_test_case.scala:1) + at illegal_access_error_test_case$.(illegal_access_error_test_case.scala) + at illegal_access_error_test_case.main(illegal_access_error_test_case.scala) + */ diff --git a/test/files/run/t6290.scala b/test/files/run/t6290.scala index 9d05db0d1885..4a5ac1cf4d14 100644 --- a/test/files/run/t6290.scala +++ b/test/files/run/t6290.scala @@ -1,4 +1,5 @@ object Test { - implicit val foo = language.dynamics + import languageFeature._ + implicit val foo: dynamics = language.dynamics def main(args: Array[String]): Unit = () } diff --git a/test/files/run/t6327.scala b/test/files/run/t6327.scala index 4a2f6c3ee88f..7ebba247b871 100644 --- a/test/files/run/t6327.scala +++ b/test/files/run/t6327.scala @@ -7,8 +7,8 @@ object Test extends App { case class R[+T](s: String) { def x() = println(s) } // Implicits in contention; StringR is nested to avoid ambiguity - object R { implicit val StringR = R[String]("A") } - implicit val Default = R[Any]("B") + object R { implicit val StringR: R[String] = R[String]("A") } + implicit val Default: R[Any] = R[Any]("B") class B() extends Dynamic { def selectDynamic[T](f: String)(implicit r: R[T]): Unit = r.x() diff --git a/test/files/run/t657.scala b/test/files/run/t657.scala index d7f882b4342a..882c9bf3fb71 100644 --- a/test/files/run/t657.scala +++ b/test/files/run/t657.scala @@ -4,7 +4,7 @@ import scala.language.{ implicitConversions } abstract class BaseList { type Node <: BaseNodeImpl - implicit def convertNode(ni : BaseNodeImpl) = ni.asInstanceOf[Node]; + implicit def convertNode(ni : BaseNodeImpl): Node = ni.asInstanceOf[Node]; abstract class BaseNodeImpl } abstract class LinkedList extends BaseList { @@ -33,7 +33,7 @@ trait Matcher extends PrecedenceParser { trait NodeImpl4 extends super.NodeImpl3 type Matchable <: Node with MatchableImpl0 - implicit def convertMatchable(m : MatchableImpl0) = m.asInstanceOf[Matchable] + implicit def convertMatchable(m : MatchableImpl0): Matchable = m.asInstanceOf[Matchable] trait MatchableImpl0 extends NodeImpl4 { override def chop : Node = { Console.println("passed"); super.chop; diff --git a/test/files/run/t8564.scala b/test/files/run/t8564.scala index 6d7d1178c4a0..6ffaa75938e2 100644 --- a/test/files/run/t8564.scala +++ b/test/files/run/t8564.scala @@ -13,8 +13,8 @@ object Test extends App { def lookup(k: String)(implicit assoc: Assoc[k.type]): assoc.V = assoc.value - implicit def nameAssoc = mkAssoc("Name", "Mary") - implicit def ageAssoc = mkAssoc("Age", 23) + implicit def nameAssoc: Assoc["Name"] { type V = String } = mkAssoc("Name", "Mary") + implicit def ageAssoc: Assoc["Age"] { type V = Int } = mkAssoc("Age", 23) assert((lookup("Name"): String) == "Mary") assert((lookup("Age"): Int) == 23) diff --git a/test/files/run/tcpoly_parseridioms.scala b/test/files/run/tcpoly_parseridioms.scala index 38455f05eebb..6144fb30dba0 100644 --- a/test/files/run/tcpoly_parseridioms.scala +++ b/test/files/run/tcpoly_parseridioms.scala @@ -103,8 +103,8 @@ trait ParserIdioms extends Parsers with Idioms { // TODO: how can parserIdiom(curry2(_)) be omitted? def expr: Parser[Expr] = parserIdiomFun(curry2(Plus)) <| num <> num |> - implicit def curry2[s, t, u](fun: (s, t) => u)(a: s)(b: t) = fun(a, b) - implicit def curry3[r, s, t, u](fun: (r, s, t) => u)(a: r)(b: s)(c: t) = fun(a, b, c) + implicit def curry2[s, t, u](fun: (s, t) => u)(a: s)(b: t): u = fun(a, b) + implicit def curry3[r, s, t, u](fun: (r, s, t) => u)(a: r)(b: s)(c: t): u = fun(a, b, c) } object Test extends ParserIdioms with App { diff --git a/test/junit/scala/io/SourceTest.scala b/test/junit/scala/io/SourceTest.scala index 0c4a996d1c9f..593b990d0413 100644 --- a/test/junit/scala/io/SourceTest.scala +++ b/test/junit/scala/io/SourceTest.scala @@ -10,7 +10,7 @@ import java.io.{Console => _, _} class SourceTest { - private implicit val `our codec` = Codec.UTF8 + private implicit val `our codec`: Codec = Codec.UTF8 private val charSet = Codec.UTF8.charSet.name private def sampler = """ diff --git a/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala b/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala index c57ca039a144..0248aebdb990 100644 --- a/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala +++ b/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala @@ -10,11 +10,11 @@ class AbstractFileClassLoaderTest { import scala.reflect.io._ import scala.io.Source - import scala.io.Codec.UTF8 + import scala.io.Codec, Codec.UTF8 import scala.reflect.io.Streamable import java.net.{ URLClassLoader, URL } - implicit def `we love utf8` = UTF8 + implicit def `we love utf8`: Codec = UTF8 implicit class `abs file ops`(f: AbstractFile) { def writeContent(s: String): Unit = Streamable.closing(f.bufferedOutput)(os => os write s.getBytes(UTF8.charSet)) } diff --git a/test/macro-annot/run/scopes/scopes_2.scala b/test/macro-annot/run/scopes/scopes_2.scala index 451931c14ee8..783c729aef1f 100644 --- a/test/macro-annot/run/scopes/scopes_2.scala +++ b/test/macro-annot/run/scopes/scopes_2.scala @@ -2,7 +2,7 @@ object Test extends App { def assertEquals(a: Any, b: Any): Unit = { assert(a == b, s"$a != $b") } - implicit val x = 42 + implicit val x: Int = 42 @explorer object C // @Test def toplevel: Unit = diff --git a/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala b/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala index 5b06a35725be..adae13349733 100644 --- a/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala +++ b/test/scalacheck/scala/collection/mutable/MutableTreeMap.scala @@ -31,8 +31,8 @@ trait Generators { } yield mutable.TreeMap(keys zip values: _*) } - implicit def arbRedBlackTree[A: Arbitrary: Ordering, B: Arbitrary] = Arbitrary(genRedBlackTree[A, B]) - implicit def arbTreeMap[A: Arbitrary: Ordering, B: Arbitrary] = Arbitrary(genTreeMap[A, B]) + implicit def arbRedBlackTree[A: Arbitrary: Ordering, B: Arbitrary]: Arbitrary[RB.Tree[A,B]] = Arbitrary(genRedBlackTree[A, B]) + implicit def arbTreeMap[A: Arbitrary: Ordering, B: Arbitrary]: Arbitrary[mutable.TreeMap[A,B]] = Arbitrary(genTreeMap[A, B]) } object RedBlackTreeProperties extends Properties("mutable.RedBlackTree") with Generators { @@ -218,7 +218,7 @@ object MutableTreeMapProjectionProperties extends Properties("mutable.TreeMapPro type K = String type V = Int - implicit val ord = implicitly[Ordering[K]] + private val ord = implicitly[Ordering[K]] def in(key: K, from: Option[K], until: Option[K]) = from.fold(true)(_ <= key) && until.fold(true)(_ > key) diff --git a/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala b/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala index 39a44ae453e7..68ee90462935 100644 --- a/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala +++ b/test/scalacheck/scala/collection/mutable/MutableTreeSet.scala @@ -117,7 +117,7 @@ object MutableTreeSetProperties extends Properties("mutable.TreeSet") { object MutableTreeSetProjectionProperties extends Properties("mutable.TreeSetProjection") { type K = String - implicit val ord = implicitly[Ordering[K]] + private val ord = implicitly[Ordering[K]] def in(key: K, from: Option[K], until: Option[K]) = from.fold(true)(_ <= key) && until.fold(true)(_ > key) diff --git a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala index 19032a2d0fb7..bc6e6089c231 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala @@ -267,8 +267,8 @@ trait ArbitraryTreesAndNames { def genTreeIsTypeWrapped(size: Int) = for(tit <- genTreeIsType(size)) yield TreeIsType(tit) - implicit val liftTreeIsTerm = Liftable[TreeIsTerm] { _.tree } - implicit val liftTreeIsType = Liftable[TreeIsType] { _.tree } + implicit val liftTreeIsTerm: Liftable[TreeIsTerm] = Liftable[TreeIsTerm] { _.tree } + implicit val liftTreeIsType: Liftable[TreeIsType] = Liftable[TreeIsType] { _.tree } implicit def treeIsTerm2tree(tit: TreeIsTerm): Tree = tit.tree implicit def treeIsType2tree(tit: TreeIsType): Tree = tit.tree diff --git a/test/scalacheck/treemap.scala b/test/scalacheck/treemap.scala index 83fb586b5192..1df3424c2064 100644 --- a/test/scalacheck/treemap.scala +++ b/test/scalacheck/treemap.scala @@ -12,7 +12,7 @@ object TreeMapTest extends Properties("TreeMap") { keys <- listOf(arbitrary[A]) values <- listOfN(keys.size, arbitrary[B]) } yield TreeMap(keys zip values: _*) - implicit def arbTreeMap[A : Arbitrary : Ordering, B : Arbitrary] = Arbitrary(genTreeMap[A, B]) + implicit def arbTreeMap[A : Arbitrary : Ordering, B : Arbitrary]: Arbitrary[TreeMap[A, B]] = Arbitrary(genTreeMap[A, B]) property("foreach/iterator consistency") = forAll { (subject: TreeMap[Int, String]) => val it = subject.iterator diff --git a/test/scaladoc/run/diagrams-base.check b/test/scaladoc/run/diagrams-base.check index 619c56180bb9..ec9c6d6a74f2 100644 --- a/test/scaladoc/run/diagrams-base.check +++ b/test/scaladoc/run/diagrams-base.check @@ -1 +1,10 @@ +newSource:10: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.T) + object E { implicit def eToT(e: E) = new T } + ^ +newSource:18: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.E) + object X { implicit def xToE(x: X) = new E} + ^ +newSource:21: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.E) + object Z { implicit def zToE(z: Z) = new E} + ^ Done. diff --git a/test/scaladoc/run/diagrams-filtering.check b/test/scaladoc/run/diagrams-filtering.check index 619c56180bb9..6f40b2c32793 100644 --- a/test/scaladoc/run/diagrams-filtering.check +++ b/test/scaladoc/run/diagrams-filtering.check @@ -1 +1,7 @@ +newSource:30: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.T) + implicit def eToT(e: E) = new T + ^ +newSource:31: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.diagrams.A) + implicit def eToA(e: E) = new A { } + ^ Done. diff --git a/test/scaladoc/run/implicits-ambiguating.check b/test/scaladoc/run/implicits-ambiguating.check index 619c56180bb9..f716066eb126 100644 --- a/test/scaladoc/run/implicits-ambiguating.check +++ b/test/scaladoc/run/implicits-ambiguating.check @@ -1 +1,7 @@ +newSource:70: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.ambiguating.X[T]) + implicit def AtoX[T](a: A[T]) = new X[T] + ^ +newSource:71: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.ambiguating.Z[T]) + implicit def AtoZ[T](a: A[T]) = new Z[T] + ^ Done. diff --git a/test/scaladoc/run/implicits-base.check b/test/scaladoc/run/implicits-base.check index 619c56180bb9..e5f9afca6ebf 100644 --- a/test/scaladoc/run/implicits-base.check +++ b/test/scaladoc/run/implicits-base.check @@ -1 +1,19 @@ +newSource:36: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.EnrichedA[V]) + implicit def enrichA0[V](a: A[V]) = new EnrichedA(a) + ^ +newSource:37: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.NumericA[ZBUR]) + implicit def enrichA1[ZBUR: Numeric](a: A[ZBUR]) = new NumericA[ZBUR](a) + ^ +newSource:38: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.IntA) + implicit def enrichA2(a: A[Int]) = new IntA(a) + ^ +newSource:39: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.GtColonDoubleA) + implicit def enrichA3(a: A[T] forSome { type T <: Double }) = new GtColonDoubleA(a) + ^ +newSource:42: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.MyNumericA[Z]) + implicit def enrichA6[Z: MyNumeric](a: A[Z]) = new MyNumericA[Z](a) + ^ +newSource:44: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.base.ManifestA[H] with scala.test.scaladoc.implicits.base.MyTraversableOps[H]) + implicit def enrichA7[H <: Double : Manifest](a: A[H]) = new ManifestA[H](a) with MyTraversableOps[H] { def convToTraversableOps(x: H): H = sys.error("no") } + ^ Done. diff --git a/test/scaladoc/run/implicits-chaining.check b/test/scaladoc/run/implicits-chaining.check index 619c56180bb9..9cbfe46b4ac1 100644 --- a/test/scaladoc/run/implicits-chaining.check +++ b/test/scaladoc/run/implicits-chaining.check @@ -1 +1,16 @@ +newSource:22: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit1[T]) + implicit def implicit1[T <: Intermediate[_, _]](implicit b: Implicit2[T]) = new Implicit1[T](b) + ^ +newSource:24: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit2[T]) + implicit def implicit2alt1[T <: Intermediate[_ <: String, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +newSource:25: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit2[T]) + implicit def implicit2alt2[T <: Intermediate[_ <: Double, _]](implicit c: Implicit3[T]) = new Implicit2[T](c) + ^ +newSource:27: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit3[T]) + implicit def implicit3alt1[T <: Intermediate[_, _ <: Int]] = new Implicit3[T]() + ^ +newSource:28: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.Implicit3[T]) + implicit def implicit3alt2[T <: Intermediate[_ <: Double, _ <: AnyRef],X] = new Implicit3[T]() + ^ Done. diff --git a/test/scaladoc/run/implicits-known-type-classes.check b/test/scaladoc/run/implicits-known-type-classes.check index 619c56180bb9..225b60e6cfe3 100644 --- a/test/scaladoc/run/implicits-known-type-classes.check +++ b/test/scaladoc/run/implicits-known-type-classes.check @@ -1 +1,49 @@ +newSource:13: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Numeric[T]]) + implicit def convertNumeric [T: Numeric] (a: A[T]) = new B(implicitly[Numeric[T]]) + ^ +newSource:14: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Integral[T]]) + implicit def convertIntegral [T: Integral] (a: A[T]) = new B(implicitly[Integral[T]]) + ^ +newSource:15: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Fractional[T]]) + implicit def convertFractional [T: Fractional] (a: A[T]) = new B(implicitly[Fractional[T]]) + ^ +newSource:16: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[Manifest[T]]) + implicit def convertManifest [T: Manifest] (a: A[T]) = new B(implicitly[Manifest[T]]) + ^ +newSource:17: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[reflect.ClassManifest[T]]) + implicit def convertClassManifest [T: ClassManifest] (a: A[T]) = new B(implicitly[ClassManifest[T]]) + ^ +newSource:18: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[OptManifest[T]]) + implicit def convertOptManifest [T: OptManifest] (a: A[T]) = new B(implicitly[OptManifest[T]]) + ^ +newSource:19: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.reflect.ClassTag[T]]) + implicit def convertClassTag [T: ClassTag] (a: A[T]) = new B(implicitly[ClassTag[T]]) + ^ +newSource:20: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[reflect.runtime.universe.TypeTag[T]]) + implicit def convertTypeTag [T: TypeTag] (a: A[T]) = new B(implicitly[TypeTag[T]]) + ^ +newSource:29: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.K[T]]) + implicit def convertK [T: K] (a: A[T]) = new B(implicitly[K[T]]) + ^ +newSource:30: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.L[T]]) + implicit def convertL [T: L] (a: A[T]) = new B(implicitly[L[T]]) + ^ +newSource:31: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.M[T]]) + implicit def convertM [T: M] (a: A[T]) = new B(implicitly[M[T]]) + ^ +newSource:32: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.N[T]]) + implicit def convertN [T: N] (a: A[T]) = new B(implicitly[N[T]]) + ^ +newSource:33: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.O[T]]) + implicit def convertO [T: O] (a: A[T]) = new B(implicitly[O[T]]) + ^ +newSource:34: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.P[T]]) + implicit def convertP [T: P] (a: A[T]) = new B(implicitly[P[T]]) + ^ +newSource:35: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.Q[T]]) + implicit def convertQ [T: Q] (a: A[T]) = new B(implicitly[Q[T]]) + ^ +newSource:36: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.typeclasses.B[scala.test.scaladoc.implicits.typeclasses.A.R[T]]) + implicit def convertR [T: R] (a: A[T]) = new B(implicitly[R[T]]) + ^ Done. diff --git a/test/scaladoc/run/implicits-shadowing.check b/test/scaladoc/run/implicits-shadowing.check index 619c56180bb9..59ba86b25cef 100644 --- a/test/scaladoc/run/implicits-shadowing.check +++ b/test/scaladoc/run/implicits-shadowing.check @@ -1 +1,4 @@ +newSource:63: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.implicits.shadowing.Z[T]) + implicit def AtoZ[T](a: A[T]) = new Z[T] + ^ Done. diff --git a/test/scaladoc/run/implicits-var-exp.check b/test/scaladoc/run/implicits-var-exp.check index 619c56180bb9..9cc519cab9e0 100644 --- a/test/scaladoc/run/implicits-var-exp.check +++ b/test/scaladoc/run/implicits-var-exp.check @@ -1 +1,7 @@ +newSource:8: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.variable.expansion.C) + implicit def aToC(a: A) = new C + ^ +newSource:9: warning: Implicit definition should have explicit type (inferred scala.test.scaladoc.variable.expansion.E with scala.test.scaladoc.variable.expansion.F) + implicit def aToE(a: A) = new E with F + ^ Done. diff --git a/test/tasty/neg/src-2/TestCtxFns_fail.scala b/test/tasty/neg/src-2/TestCtxFns_fail.scala index 150ec4a209bc..c1db5e1d1585 100644 --- a/test/tasty/neg/src-2/TestCtxFns_fail.scala +++ b/test/tasty/neg/src-2/TestCtxFns_fail.scala @@ -9,7 +9,7 @@ object TestCtxFns { def puts[T](t: T): Unit = logs += t.toString } - implicit val ctx = new TestContext + implicit val ctx: TestContext = new TestContext def test1: Unit = { puts(23)(ctx) From 6053dbe110748f05cb4d7efde9d8f12ca745ba04 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 22 Nov 2022 17:35:53 -0800 Subject: [PATCH 074/261] Implicit type warning is cat=other-implicit-type --- src/compiler/scala/tools/nsc/Reporting.scala | 1 + .../tools/nsc/typechecker/ContextErrors.scala | 2 +- test/files/pos/t5265a.scala | 32 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t5265a.scala diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index fc1abc6d6691..fd234ac32866 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -362,6 +362,7 @@ object Reporting { object OtherDebug extends Other; add(OtherDebug) object OtherNullaryOverride extends Other; add(OtherNullaryOverride) object OtherNonCooperativeEquals extends Other; add(OtherNonCooperativeEquals) + object OtherImplicitType extends Other; add(OtherImplicitType) sealed trait WFlag extends WarningCategory { override def summaryCategory: WarningCategory = WFlag } object WFlag extends WFlag { override def includes(o: WarningCategory): Boolean = o.isInstanceOf[WFlag] }; add(WFlag) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 0b56ec5495b7..b7b60e6c3136 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -193,7 +193,7 @@ trait ContextErrors extends splain.SplainErrors { if (!inferred.isErroneous) s" (inferred $inferred)" else "" }" if (currentRun.isScala3) ErrorUtils.issueNormalTypeError(tree, msg)(cx) - else cx.warning(tree.pos, msg, WarningCategory.Other) + else cx.warning(tree.pos, msg, WarningCategory.OtherImplicitType) } val sym = tree.symbol // Defer warning field of class until typing getter (which is marked implicit) diff --git a/test/files/pos/t5265a.scala b/test/files/pos/t5265a.scala new file mode 100644 index 000000000000..95db6ec743ed --- /dev/null +++ b/test/files/pos/t5265a.scala @@ -0,0 +1,32 @@ +// scalac: -Werror -Wconf:cat=other-implicit-type:s +trait T[A] + +class C[A: T] + +trait Missing { + implicit val tsMissing = new T[String] {} // warn val in trait + def f = new C[String] +} +trait Local { + def f = { + implicit val tsLocal = new T[String] {} // nowarn because local + new C[String] + } +} +trait Parent { + def t: T[String] +} +trait Child extends Parent { + implicit val tsChild = new T[String] {} // warn because inferred from RHS + def f = new C[String] + implicit private[this] val pChild = 42 // also warn +} +class D { + implicit val tsD = new T[String] {} // warn val in class + def f = new C[String] + implicit private[this] val y = 42 // also warn +} +class X extends Missing +trait Z { + val z = 42 +} From 8dfd7f015d9a4eb82f2b2e681c11f6673d591758 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 24 Nov 2021 10:33:39 -0800 Subject: [PATCH 075/261] Allow companion access boundary When overriding `protected[C]`, `C` may refer to the enclosing companion of `C`, which provides the same access guarantees. Update spec to allow override protected[C] companion. --- spec/05-classes-and-objects.md | 10 +- .../tools/nsc/typechecker/RefChecks.scala | 55 +++--- .../nsc/typechecker/SuperAccessors.scala | 43 ++--- .../scala/tools/partest/nest/Runner.scala | 13 +- .../util/StripMarginInterpolator.scala | 19 +- test/files/neg/t12349.check | 169 +++++++++++------- test/files/neg/t12349/t12349a.java | 8 + test/files/neg/t12349/t12349b.scala | 24 ++- test/files/neg/t12349/t12349c.scala | 26 +-- test/files/neg/t12494.check | 164 +++++++++++++++++ test/files/neg/t12494.scala | 54 ++++++ test/files/neg/t4762.check | 4 +- test/files/neg/t8525.check | 2 +- test/files/neg/t8610.check | 2 +- test/files/run/t12494.scala | 24 +++ 15 files changed, 459 insertions(+), 158 deletions(-) create mode 100644 test/files/neg/t12494.check create mode 100644 test/files/neg/t12494.scala create mode 100644 test/files/run/t12494.scala diff --git a/spec/05-classes-and-objects.md b/spec/05-classes-and-objects.md index 5c3a74e608a2..c83de4a2836d 100644 --- a/spec/05-classes-and-objects.md +++ b/spec/05-classes-and-objects.md @@ -332,13 +332,13 @@ Furthermore, the following restrictions on modifiers apply to ´M´ and - ´M'´ must not be labeled `final`. - ´M´ must not be [`private`](#modifiers). -- If ´M´ is labeled `private[´C´]` for some enclosing class or package ´C´, - then ´M'´ must be labeled `private[´C'´]` for some class or package ´C'´ where - ´C'´ equals ´C´ or ´C'´ is contained in ´C´. - - - If ´M´ is labeled `protected`, then ´M'´ must also be labeled `protected`. +- If ´M´ is labeled `private[´C´]` (respectively `protected[´C´]`) + for some enclosing class or package ´C´, + then ´M'´ must be labeled `private[´C'´]` (or, respectively, `protected[´C'´]`) + for some class or package ´C'´ where + ´C'´ equals ´C´ or the companion of ´C´, or ´C'´ is contained in ´C´. - If ´M'´ is not an abstract member, then ´M´ must be labeled `override`. Furthermore, one of two possibilities must hold: - either ´M´ is defined in a subclass of the class where is ´M'´ is defined, diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index c8d9651d819e..eaf579acc418 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -354,19 +354,23 @@ abstract class RefChecks extends Transform { overrideErrorWithMemberInfo("`override` modifier required to override concrete member:") } - def overrideAccessError(): Unit = { - val otherAccess = accessFlagsToString(other) - overrideError("weaker access privileges in overriding\n"+ - overriddenWithAddendum(s"override should ${(if (otherAccess == "") "be public" else "at least be " + otherAccess)}")) - } + def weakerAccessError(advice: String): Unit = + overrideError(sm"""|weaker access privileges in overriding + |${overriddenWithAddendum(advice)}""") + def overrideAccessError(): Unit = + weakerAccessError { + accessFlagsToString(other) match { + case "" => "override should be public" + case otherAccess => s"override should at least be $otherAccess" + } + } //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG /* Is the intersection between given two lists of overridden symbols empty? */ - def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) = - !(syms1 exists (syms2 contains _)) + def intersectionIsEmpty(syms1: List[Symbol], syms2: List[Symbol]) = !syms1.exists(syms2.contains) - if (memberClass == ObjectClass && otherClass == AnyClass) {} // skip -- can we have a mode of symbolpairs where this pair doesn't even appear? + if (memberClass == ObjectClass && otherClass == AnyClass) () // skip -- can we have a mode of symbolpairs where this pair doesn't even appear? else if (typesOnly) checkOverrideTypes() else { // o: public | protected | package-protected (aka java's default access) @@ -374,17 +378,23 @@ abstract class RefChecks extends Transform { // m: public | public/protected | public/protected/package-protected-in-same-package-as-o if (member.isPrivate) // (1.1) - overrideError("weaker access privileges in overriding\n"+ - overriddenWithAddendum(s"override should not be private")) + weakerAccessError("override should not be private") // todo: align accessibility implication checking with isAccessible in Contexts - val ob = other.accessBoundary(memberClass) - val mb = member.accessBoundary(memberClass) - def isOverrideAccessOK = member.isPublic || { // member is public, definitely same or relaxed access - (!other.isProtected || member.isProtected) && // if o is protected, so is m - ((!isRootOrNone(ob) && ob.hasTransOwner(mb)) || // m relaxes o's access boundary - (other.isJavaDefined && other.isProtected)) // overriding a protected java member, see #3946 #12349 + @inline def protectedOK = !other.isProtected || member.isProtected + @inline def accessBoundaryOK = { + val ob = other.accessBoundary(memberClass) + val mb = member.accessBoundary(memberClass) + @inline def companionBoundaryOK = ob.isClass && mb.isModuleClass && mb.module == ob.companionSymbol + !isRootOrNone(ob) && (ob.hasTransOwner(mb) || companionBoundaryOK) } + @inline def otherIsJavaProtected = other.isJavaDefined && other.isProtected + def isOverrideAccessOK = + member.isPublic || // member is public, definitely same or relaxed access + protectedOK && // if o is protected, so is m + (accessBoundaryOK || // m relaxes o's access boundary + otherIsJavaProtected // overriding a protected java member, see #3946 #12349 + ) if (!isOverrideAccessOK) { overrideAccessError() } else if (other.isClass) { @@ -753,10 +763,8 @@ abstract class RefChecks extends Transform { lazy val varargsType = toJavaRepeatedParam(member.tpe) def isSignatureMatch(sym: Symbol) = !sym.isTerm || { - val symtpe = clazz.thisType memberType sym - def matches(tp: Type) = tp matches symtpe - - matches(member.tpe) || (isVarargs && matches(varargsType)) + val symtpe = clazz.thisType.memberType(sym) + member.tpe.matches(symtpe) || (isVarargs && varargsType.matches(symtpe)) } /* The rules for accessing members which have an access boundary are more * restrictive in java than scala. Since java has no concept of package nesting, @@ -781,15 +789,16 @@ abstract class RefChecks extends Transform { || sym.isProtected // marked protected in java, thus accessible to subclasses || sym.privateWithin == member.enclosingPackageClass // exact package match ) - def classDecls = inclazz.info.nonPrivateDecl(member.name) - def matchingSyms = classDecls filter (sym => isSignatureMatch(sym) && javaAccessCheck(sym)) + def classDecl = inclazz.info.nonPrivateDecl(member.name) + .orElse(inclazz.info.nonPrivateDecl(member.unexpandedName)) + def matchingSyms = classDecl.filter(sym => isSignatureMatch(sym) && javaAccessCheck(sym)) (inclazz != clazz) && (matchingSyms != NoSymbol) } // 4. Check that every defined member with an `override` modifier overrides some other member. for (member <- clazz.info.decls) - if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) { + if (member.isAnyOverride && !clazz.thisType.baseClasses.exists(hasMatchingSym(_, member))) { // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG val nonMatching: List[Symbol] = clazz.info.member(member.name).alternatives.filterNot(_.owner == clazz).filterNot(_.isFinal) diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index ef168e5926c9..0652bcd3d76a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -117,15 +117,14 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT atPos(sel.pos)(Select(gen.mkAttributedThis(clazz), superAcc) setType sel.tpe) } - private def transformArgs(params: List[Symbol], args: List[Tree]) = { + private def transformArgs(params: List[Symbol], args: List[Tree]) = treeInfo.mapMethodParamsAndArgs(params, args) { (param, arg) => if (isByNameParamType(param.tpe)) withInvalidOwner(transform(arg)) else transform(arg) } - } - /** Check that a class and its companion object to not both define + /** Check that a class and its companion object do not both define * a class or module with same name */ private def checkCompanionNameClashes(sym: Symbol) = @@ -136,9 +135,9 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT if (other == NoSymbol) other = linked.info.decl(sym.name.toTermName).filter(_.isModule) if (other != NoSymbol) - reporter.error(sym.pos, "name clash: "+sym.owner+" defines "+sym+ - "\nand its companion "+sym.owner.companionModule+" also defines "+ - other) + reporter.error(sym.pos, + sm"""|name clash: ${sym.owner} defines $sym + |and its companion ${sym.owner.companionModule} also defines $other""") } } @@ -240,13 +239,13 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val decls = sym.info.decls for (s <- decls) { val privateWithin = s.privateWithin - if (privateWithin.isClass && !s.hasFlag(EXPANDEDNAME | PROTECTED) && !privateWithin.isModuleClass && - !s.isConstructor) { + def isPrivateWithinNonCompanionModule = privateWithin.isModuleClass + if (privateWithin.isClass && !s.hasFlag(EXPANDEDNAME | PROTECTED) && !isPrivateWithinNonCompanionModule && !s.isConstructor) { val savedName = s.name decls.unlink(s) s.expandName(privateWithin) decls.enter(s) - log("Expanded '%s' to '%s' in %s".format(savedName, s.name, sym)) + log(s"Expanded '$savedName' to '${s.name}' in $sym") } } super.transform(tree) @@ -303,23 +302,15 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT // also exists in a superclass, because they may be surprised // to find out that a constructor parameter will shadow a // field. See scala/bug#4762. - if (settings.warnPrivateShadow) { - if (sym.isPrivateLocal && sym.paramss.isEmpty) { - qual.symbol.ancestors foreach { parent => - parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 => - if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { - runReporting.warning(sel.pos, - sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name - + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " - + sym.owner + " - you may want to give them distinct names.", - WarningCategory.LintPrivateShadow, - currentOwner) - } - } - } - } - } - + if (settings.warnPrivateShadow && sym.isPrivateLocal && sym.paramss.isEmpty) + for (parent <- qual.symbol.ancestors) + for (m2 <- parent.info.decls) + if (!m2.isPrivate && !m2.isLocalToThis && sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) + runReporting.warning(sel.pos, + sq"""${sym.accessString} ${sym.fullLocationString} shadows mutable ${m2.name} inherited from ${m2.owner}. + > Changes to ${m2.name} will not be visible within ${sym.owner}; you may want to give them distinct names.""", + WarningCategory.LintPrivateShadow, + currentOwner) def isAccessibleFromSuper(sym: Symbol) = { val pre = SuperType(sym.owner.tpe, qual.tpe) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 442f95ad1d58..17e672203190 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -664,18 +664,13 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { runTestCommon(if (suiteRunner.config.optNoExec) noexec() else exec().andAlso(diffIsOk)) } - private def decompileClass(clazz: Class[_], isPackageObject: Boolean): String = { - import scala.tools.scalap - import scalap.scalax.rules.scalasig.ByteCode - - scalap.Main.decompileScala(ByteCode.forClass(clazz).bytes, isPackageObject) - } - def runScalapTest(): TestState = runTestCommon { - val isPackageObject = testFile.getName startsWith "package" + import scala.tools.scalap, scalap.scalax.rules.scalasig.ByteCode, scalap.Main.decompileScala + val isPackageObject = testFile.getName.startsWith("package") val className = testFile.getName.stripSuffix(".scala").capitalize + (if (!isPackageObject) "" else ".package") val loader = ScalaClassLoader.fromURLs(List(outDir.toURI.toURL), this.getClass.getClassLoader) - logFile writeAll decompileClass(loader loadClass className, isPackageObject) + def decompileClass(clazz: Class[_]): String = decompileScala(ByteCode.forClass(clazz).bytes, isPackageObject) + logFile.writeAll(decompileClass(loader.loadClass(className))) diffIsOk } diff --git a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala index 16a265dd4677..5ef032b94cc5 100644 --- a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala +++ b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala @@ -23,7 +23,7 @@ trait StripMarginInterpolator { * and [[scala.StringContext#raw]]. * * The margin of each line is defined by whitespace leading up to a '|' character. - * This margin is stripped '''before''' the arguments are interpolated into to string. + * This margin is stripped '''before''' the arguments are interpolated into the string. * * String escape sequences are '''not''' processed; this interpolator is designed to * be used with triple quoted Strings. @@ -31,23 +31,30 @@ trait StripMarginInterpolator { * {{{ * scala> val foo = "f|o|o" * foo: String = f|o|o - * scala> sm"""|${foo} + * scala> sm"""|${foo}| * |""" * res0: String = - * "f|o|o + * "f|o|o| * " * }}} */ - final def sm(args: Any*): String = { + final def sm(args: Any*): String = impl('|', args: _*) + + private final def impl(sep: Char, args: Any*): String = { def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringOps#isLineBreak def stripTrailingPart(s: String) = { val (pre, post) = s.span(c => !isLineBreak(c)) - pre + post.stripMargin + pre + post.stripMargin(sep) } val stripped: List[String] = stringContext.parts.toList match { - case head :: tail => head.stripMargin :: (tail map stripTrailingPart) + case head :: tail => head.stripMargin(sep) :: tail.map(stripTrailingPart) case Nil => Nil } new StringContext(stripped: _*).raw(args: _*) } + + /** Like the `sm` interpolator, but strips quotation-style delimiter `>` + * and merges the resulting lines into a single line string. + */ + final def sq(args: Any*): String = impl('>', args: _*).linesIterator.mkString } diff --git a/test/files/neg/t12349.check b/test/files/neg/t12349.check index ed6d1b26451d..e774d33bd3a0 100644 --- a/test/files/neg/t12349.check +++ b/test/files/neg/t12349.check @@ -31,129 +31,150 @@ def a7(): Unit (defined in class t12349a) t12349b.scala:13: error: weaker access privileges in overriding def a8(): Unit (defined in class t12349a) override should be public - protected[this] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + protected[Inner12349b] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + ^ +t12349b.scala:15: error: weaker access privileges in overriding +def aA(): Unit (defined in class t12349a) + override should be public + protected[this] override def aA(): Unit = println("Inner12349b#aA()") // weaker access privileges ^ -t12349b.scala:14: error: weaker access privileges in overriding -def a9(): Unit (defined in class t12349a) +t12349b.scala:16: error: weaker access privileges in overriding +def aB(): Unit (defined in class t12349a) override should not be private - private[this] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349b#aB()") // weaker access privileges ^ -t12349b.scala:18: error: weaker access privileges in overriding +t12349b.scala:20: error: weaker access privileges in overriding protected[package t12349] def b3(): Unit (defined in class t12349a) override should not be private private override def b3(): Unit = println("Inner12349b#b3()") // weaker access privileges ^ -t12349b.scala:20: error: weaker access privileges in overriding +t12349b.scala:22: error: weaker access privileges in overriding protected[package t12349] def b5(): Unit (defined in class t12349a) override should at least be protected[t12349] private[t12349b] override def b5(): Unit = println("Inner12349b#b5()") // weaker access privileges ^ -t12349b.scala:22: error: weaker access privileges in overriding +t12349b.scala:24: error: weaker access privileges in overriding protected[package t12349] def b7(): Unit (defined in class t12349a) override should at least be protected[t12349] private[t12349] override def b7(): Unit = println("Inner12349b#b7()") // weaker access privileges ^ -t12349b.scala:24: error: weaker access privileges in overriding -protected[package t12349] def b9(): Unit (defined in class t12349a) +t12349b.scala:28: error: weaker access privileges in overriding +protected[package t12349] def bB(): Unit (defined in class t12349a) override should not be private - private[this] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + private[this] override def bB(): Unit = println("Inner12349b#bB()") // weaker access privileges ^ -t12349b.scala:27: error: weaker access privileges in overriding +t12349b.scala:31: error: weaker access privileges in overriding private[package t12349] def c2(): Unit (defined in class t12349a) override should at least be private[t12349] protected override def c2(): Unit = println("Inner12349b#c2()") // weaker access privileges ^ -t12349b.scala:28: error: weaker access privileges in overriding +t12349b.scala:32: error: weaker access privileges in overriding private[package t12349] def c3(): Unit (defined in class t12349a) override should not be private private override def c3(): Unit = println("Inner12349b#c3()") // weaker access privileges ^ -t12349b.scala:29: error: weaker access privileges in overriding +t12349b.scala:33: error: weaker access privileges in overriding private[package t12349] def c4(): Unit (defined in class t12349a) override should at least be private[t12349] protected[t12349b] override def c4(): Unit = println("Inner12349b#c4()") // weaker access privileges ^ -t12349b.scala:30: error: weaker access privileges in overriding +t12349b.scala:34: error: weaker access privileges in overriding private[package t12349] def c5(): Unit (defined in class t12349a) override should at least be private[t12349] private[t12349b] override def c5(): Unit = println("Inner12349b#c5()") // weaker access privileges ^ -t12349b.scala:33: error: weaker access privileges in overriding +t12349b.scala:37: error: weaker access privileges in overriding private[package t12349] def c8(): Unit (defined in class t12349a) override should at least be private[t12349] - protected[this] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + protected[Inner12349b] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + ^ +t12349b.scala:39: error: weaker access privileges in overriding +private[package t12349] def cA(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[this] override def cA(): Unit = println("Inner12349b#cA()") // weaker access privileges ^ -t12349b.scala:34: error: weaker access privileges in overriding -private[package t12349] def c9(): Unit (defined in class t12349a) +t12349b.scala:40: error: weaker access privileges in overriding +private[package t12349] def cB(): Unit (defined in class t12349a) override should not be private - private[this] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + private[this] override def cB(): Unit = println("Inner12349b#cB()") // weaker access privileges ^ -t12349b.scala:36: error: method d1 overrides nothing +t12349b.scala:42: error: method d1 overrides nothing override def d1(): Unit = println("Inner12349b#d1()") // overrides nothing ^ -t12349b.scala:37: error: method d2 overrides nothing +t12349b.scala:43: error: method d2 overrides nothing protected override def d2(): Unit = println("Inner12349b#d2()") // overrides nothing ^ -t12349b.scala:38: error: method d3 overrides nothing +t12349b.scala:44: error: method d3 overrides nothing private override def d3(): Unit = println("Inner12349b#d3()") // overrides nothing ^ -t12349b.scala:39: error: method d4 overrides nothing +t12349b.scala:45: error: method d4 overrides nothing protected[t12349b] override def d4(): Unit = println("Inner12349b#d4()") // overrides nothing ^ -t12349b.scala:40: error: method d5 overrides nothing +t12349b.scala:46: error: method d5 overrides nothing private[t12349b] override def d5(): Unit = println("Inner12349b#d5()") // overrides nothing ^ -t12349b.scala:41: error: method d6 overrides nothing +t12349b.scala:47: error: method d6 overrides nothing protected[t12349] override def d6(): Unit = println("Inner12349b#d6()") // overrides nothing ^ -t12349b.scala:42: error: method d7 overrides nothing +t12349b.scala:48: error: method d7 overrides nothing private[t12349] override def d7(): Unit = println("Inner12349b#d7()") // overrides nothing ^ -t12349b.scala:43: error: method d8 overrides nothing - protected[this] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing +t12349b.scala:49: error: method d8 overrides nothing + protected[Inner12349b] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing + ^ +t12349b.scala:51: error: method dA overrides nothing + protected[this] override def dA(): Unit = println("Inner12349b#dA()") // overrides nothing ^ -t12349b.scala:44: error: method d9 overrides nothing - private[this] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing +t12349b.scala:52: error: method dB overrides nothing + private[this] override def dB(): Unit = println("Inner12349b#dB()") // overrides nothing ^ -t12349c.scala:11: error: weaker access privileges in overriding +t12349b.scala:50: error: method d9 overrides nothing + private[Inner12349b] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + ^ +t12349c.scala:9: error: weaker access privileges in overriding def a2(): Unit (defined in class t12349a) override should be public protected override def a2(): Unit = println("Inner12349c#a2()") // weaker access privileges ^ -t12349c.scala:12: error: weaker access privileges in overriding +t12349c.scala:10: error: weaker access privileges in overriding def a3(): Unit (defined in class t12349a) override should not be private private override def a3(): Unit = println("Inner12349c#a3()") // weaker access privileges ^ -t12349c.scala:13: error: weaker access privileges in overriding +t12349c.scala:11: error: weaker access privileges in overriding def a4(): Unit (defined in class t12349a) override should be public protected[t12349c] override def a4(): Unit = println("Inner12349c#a4()") // weaker access privileges ^ -t12349c.scala:14: error: weaker access privileges in overriding +t12349c.scala:12: error: weaker access privileges in overriding def a5(): Unit (defined in class t12349a) override should be public private[t12349c] override def a5(): Unit = println("Inner12349c#a5()") // weaker access privileges ^ -t12349c.scala:15: error: weaker access privileges in overriding +t12349c.scala:13: error: weaker access privileges in overriding def a6(): Unit (defined in class t12349a) override should be public protected[pkg] override def a6(): Unit = println("Inner12349c#a6()") // weaker access privileges ^ -t12349c.scala:16: error: weaker access privileges in overriding +t12349c.scala:14: error: weaker access privileges in overriding def a7(): Unit (defined in class t12349a) override should be public private[pkg] override def a7(): Unit = println("Inner12349c#a7()") // weaker access privileges ^ -t12349c.scala:17: error: weaker access privileges in overriding +t12349c.scala:15: error: weaker access privileges in overriding def a8(): Unit (defined in class t12349a) override should be public - protected[this] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + protected[Inner12349c] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + ^ +t12349c.scala:17: error: weaker access privileges in overriding +def aA(): Unit (defined in class t12349a) + override should be public + protected[this] override def aA(): Unit = println("Inner12349c#aA()") // weaker access privileges ^ t12349c.scala:18: error: weaker access privileges in overriding -def a9(): Unit (defined in class t12349a) +def aB(): Unit (defined in class t12349a) override should not be private - private[this] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349c#aB()") // weaker access privileges ^ t12349c.scala:22: error: weaker access privileges in overriding protected[package t12349] def b3(): Unit (defined in class t12349a) @@ -170,79 +191,93 @@ protected[package t12349] def b7(): Unit (defined in class t12349a) override should at least be protected[t12349] private[pkg] override def b7(): Unit = println("Inner12349c#b7()") // weaker access privileges ^ -t12349c.scala:28: error: weaker access privileges in overriding -protected[package t12349] def b9(): Unit (defined in class t12349a) +t12349c.scala:30: error: weaker access privileges in overriding +protected[package t12349] def bB(): Unit (defined in class t12349a) override should not be private - private[this] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + private[this] override def bB(): Unit = println("Inner12349c#bB()") // weaker access privileges ^ -t12349c.scala:31: error: weaker access privileges in overriding +t12349c.scala:33: error: weaker access privileges in overriding private[package t12349] def c2(): Unit (defined in class t12349a) override should at least be private[t12349] protected override def c2(): Unit = println("Inner12349c#c2()") // weaker access privileges ^ -t12349c.scala:32: error: weaker access privileges in overriding +t12349c.scala:34: error: weaker access privileges in overriding private[package t12349] def c3(): Unit (defined in class t12349a) override should not be private private override def c3(): Unit = println("Inner12349c#c3()") // weaker access privileges ^ -t12349c.scala:33: error: weaker access privileges in overriding +t12349c.scala:35: error: weaker access privileges in overriding private[package t12349] def c4(): Unit (defined in class t12349a) override should at least be private[t12349] protected[t12349c] override def c4(): Unit = println("Inner12349c#c4()") // weaker access privileges ^ -t12349c.scala:34: error: weaker access privileges in overriding +t12349c.scala:36: error: weaker access privileges in overriding private[package t12349] def c5(): Unit (defined in class t12349a) override should at least be private[t12349] private[t12349c] override def c5(): Unit = println("Inner12349c#c5()") // weaker access privileges ^ -t12349c.scala:35: error: weaker access privileges in overriding +t12349c.scala:37: error: weaker access privileges in overriding private[package t12349] def c6(): Unit (defined in class t12349a) override should at least be private[t12349] protected[pkg] override def c6(): Unit = println("Inner12349c#c6()") // weaker access privileges ^ -t12349c.scala:36: error: weaker access privileges in overriding +t12349c.scala:38: error: weaker access privileges in overriding private[package t12349] def c7(): Unit (defined in class t12349a) override should at least be private[t12349] private[pkg] override def c7(): Unit = println("Inner12349c#c7()") // weaker access privileges ^ -t12349c.scala:37: error: weaker access privileges in overriding +t12349c.scala:39: error: weaker access privileges in overriding private[package t12349] def c8(): Unit (defined in class t12349a) override should at least be private[t12349] - protected[this] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + protected[Inner12349c] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + ^ +t12349c.scala:41: error: weaker access privileges in overriding +private[package t12349] def cA(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[this] override def cA(): Unit = println("Inner12349c#cA()") // weaker access privileges ^ -t12349c.scala:38: error: weaker access privileges in overriding -private[package t12349] def c9(): Unit (defined in class t12349a) +t12349c.scala:42: error: weaker access privileges in overriding +private[package t12349] def cB(): Unit (defined in class t12349a) override should not be private - private[this] override def c9(): Unit = println("Inner12349c#c9()") // overrides nothing (invisible) + private[this] override def cB(): Unit = println("Inner12349c#cB()") // weaker access privileges ^ -t12349c.scala:30: error: method c1 overrides nothing +t12349c.scala:32: error: method c1 overrides nothing override def c1(): Unit = println("Inner12349c#c1()") // overrides nothing (invisible) ^ -t12349c.scala:40: error: method d1 overrides nothing +t12349c.scala:44: error: method d1 overrides nothing override def d1(): Unit = println("Inner12349c#d1()") // overrides nothing ^ -t12349c.scala:41: error: method d2 overrides nothing +t12349c.scala:45: error: method d2 overrides nothing protected override def d2(): Unit = println("Inner12349c#d2()") // overrides nothing ^ -t12349c.scala:42: error: method d3 overrides nothing +t12349c.scala:46: error: method d3 overrides nothing private override def d3(): Unit = println("Inner12349c#d3()") // overrides nothing ^ -t12349c.scala:43: error: method d4 overrides nothing +t12349c.scala:47: error: method d4 overrides nothing protected[t12349c] override def d4(): Unit = println("Inner12349c#d4()") // overrides nothing ^ -t12349c.scala:44: error: method d5 overrides nothing +t12349c.scala:48: error: method d5 overrides nothing private[t12349c] override def d5(): Unit = println("Inner12349c#d5()") // overrides nothing ^ -t12349c.scala:45: error: method d6 overrides nothing +t12349c.scala:49: error: method d6 overrides nothing protected[pkg] override def d6(): Unit = println("Inner12349c#d6()") // overrides nothing ^ -t12349c.scala:46: error: method d7 overrides nothing +t12349c.scala:50: error: method d7 overrides nothing private[pkg] override def d7(): Unit = println("Inner12349c#d7()") // overrides nothing ^ -t12349c.scala:47: error: method d8 overrides nothing - protected[this] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing +t12349c.scala:51: error: method d8 overrides nothing + protected[Inner12349c] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing + ^ +t12349c.scala:53: error: method dA overrides nothing + protected[this] override def dA(): Unit = println("Inner12349c#dA()") // overrides nothing ^ -t12349c.scala:48: error: method d9 overrides nothing - private[this] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing +t12349c.scala:54: error: method dB overrides nothing + private[this] override def dB(): Unit = println("Inner12349c#dB()") // overrides nothing ^ -57 errors +t12349c.scala:40: error: method c9 overrides nothing + private[Inner12349c] override def c9(): Unit = println("Inner12349c#c9()") // weaker access privileges + ^ +t12349c.scala:52: error: method d9 overrides nothing + private[Inner12349c] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + ^ +66 errors diff --git a/test/files/neg/t12349/t12349a.java b/test/files/neg/t12349/t12349a.java index db9de0b0a539..f1b2cb51af39 100644 --- a/test/files/neg/t12349/t12349a.java +++ b/test/files/neg/t12349/t12349a.java @@ -11,6 +11,8 @@ public class t12349a { public void a7() { System.out.println("t12349a#a7()"); } public void a8() { System.out.println("t12349a#a8()"); } public void a9() { System.out.println("t12349a#a9()"); } + public void aA() { System.out.println("t12349a#aA()"); } + public void aB() { System.out.println("t12349a#aB()"); } protected void b1() { System.out.println("t12349a#b1()"); } protected void b2() { System.out.println("t12349a#b2()"); } @@ -21,6 +23,8 @@ public class t12349a { protected void b7() { System.out.println("t12349a#b7()"); } protected void b8() { System.out.println("t12349a#b8()"); } protected void b9() { System.out.println("t12349a#b9()"); } + protected void bA() { System.out.println("t12349a#bA()"); } + protected void bB() { System.out.println("t12349a#bB()"); } void c1() { System.out.println("t12349a#c1()"); } void c2() { System.out.println("t12349a#c2()"); } @@ -31,6 +35,8 @@ public class t12349a { void c7() { System.out.println("t12349a#c7()"); } void c8() { System.out.println("t12349a#c8()"); } void c9() { System.out.println("t12349a#c9()"); } + void cA() { System.out.println("t12349a#cA()"); } + void cB() { System.out.println("t12349a#cB()"); } private void d1() { System.out.println("t12349a#d1()"); } private void d2() { System.out.println("t12349a#d2()"); } @@ -41,5 +47,7 @@ public class t12349a { private void d7() { System.out.println("t12349a#d7()"); } private void d8() { System.out.println("t12349a#d8()"); } private void d9() { System.out.println("t12349a#d9()"); } + private void dA() { System.out.println("t12349a#dA()"); } + private void dB() { System.out.println("t12349a#dB()"); } } diff --git a/test/files/neg/t12349/t12349b.scala b/test/files/neg/t12349/t12349b.scala index 38b3309779b3..2d72c8124c32 100644 --- a/test/files/neg/t12349/t12349b.scala +++ b/test/files/neg/t12349/t12349b.scala @@ -10,8 +10,10 @@ object t12349b { private[t12349b] override def a5(): Unit = println("Inner12349b#a5()") // weaker access privileges protected[t12349] override def a6(): Unit = println("Inner12349b#a6()") // weaker access privileges private[t12349] override def a7(): Unit = println("Inner12349b#a7()") // weaker access privileges - protected[this] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges - private[this] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + protected[Inner12349b] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + private[Inner12349b] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + protected[this] override def aA(): Unit = println("Inner12349b#aA()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349b#aB()") // weaker access privileges override def b1(): Unit = println("Inner12349b#b1()") protected override def b2(): Unit = println("Inner12349b#b2()") @@ -20,8 +22,10 @@ object t12349b { private[t12349b] override def b5(): Unit = println("Inner12349b#b5()") // weaker access privileges protected[t12349] override def b6(): Unit = println("Inner12349b#b6()") private[t12349] override def b7(): Unit = println("Inner12349b#b7()") // weaker access privileges - protected[this] override def b8(): Unit = println("Inner12349b#b8()") // [#12349] - not fixed by PR #9525 - private[this] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + protected[Inner12349b] override def b8(): Unit = println("Inner12349b#b8()") // [#12349] - not fixed by PR #9525 + private[Inner12349b] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + protected[this] override def bA(): Unit = println("Inner12349b#bA()") // [#12349] - not fixed by PR #9525 + private[this] override def bB(): Unit = println("Inner12349b#bB()") // weaker access privileges override def c1(): Unit = println("Inner12349b#c1()") protected override def c2(): Unit = println("Inner12349b#c2()") // weaker access privileges @@ -30,8 +34,10 @@ object t12349b { private[t12349b] override def c5(): Unit = println("Inner12349b#c5()") // weaker access privileges protected[t12349] override def c6(): Unit = println("Inner12349b#c6()") private[t12349] override def c7(): Unit = println("Inner12349b#c7()") - protected[this] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges - private[this] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + protected[Inner12349b] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + private[Inner12349b] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + protected[this] override def cA(): Unit = println("Inner12349b#cA()") // weaker access privileges + private[this] override def cB(): Unit = println("Inner12349b#cB()") // weaker access privileges override def d1(): Unit = println("Inner12349b#d1()") // overrides nothing protected override def d2(): Unit = println("Inner12349b#d2()") // overrides nothing @@ -40,8 +46,10 @@ object t12349b { private[t12349b] override def d5(): Unit = println("Inner12349b#d5()") // overrides nothing protected[t12349] override def d6(): Unit = println("Inner12349b#d6()") // overrides nothing private[t12349] override def d7(): Unit = println("Inner12349b#d7()") // overrides nothing - protected[this] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing - private[this] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + protected[Inner12349b] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing + private[Inner12349b] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + protected[this] override def dA(): Unit = println("Inner12349b#dA()") // overrides nothing + private[this] override def dB(): Unit = println("Inner12349b#dB()") // overrides nothing } } diff --git a/test/files/neg/t12349/t12349c.scala b/test/files/neg/t12349/t12349c.scala index 942991a22430..e86cfd9a79bf 100644 --- a/test/files/neg/t12349/t12349c.scala +++ b/test/files/neg/t12349/t12349c.scala @@ -1,7 +1,5 @@ package t12349 -import t12349.t12349a - package pkg { object t12349c { @@ -14,8 +12,10 @@ package pkg { private[t12349c] override def a5(): Unit = println("Inner12349c#a5()") // weaker access privileges protected[pkg] override def a6(): Unit = println("Inner12349c#a6()") // weaker access privileges private[pkg] override def a7(): Unit = println("Inner12349c#a7()") // weaker access privileges - protected[this] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges - private[this] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + protected[Inner12349c] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + private[Inner12349c] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + protected[this] override def aA(): Unit = println("Inner12349c#aA()") // weaker access privileges + private[this] override def aB(): Unit = println("Inner12349c#aB()") // weaker access privileges override def b1(): Unit = println("Inner12349c#b1()") protected override def b2(): Unit = println("Inner12349c#b2()") @@ -24,8 +24,10 @@ package pkg { private[t12349c] override def b5(): Unit = println("Inner12349c#b5()") // weaker access privileges protected[pkg] override def b6(): Unit = println("Inner12349c#b6()") private[pkg] override def b7(): Unit = println("Inner12349c#b7()") // weaker access privileges - protected[this] override def b8(): Unit = println("Inner12349c#b8()") // [#12349] - not fixed by PR #9525 - private[this] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + protected[Inner12349c] override def b8(): Unit = println("Inner12349c#b8()") // [#12349] - not fixed by PR #9525 + private[Inner12349c] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + protected[this] override def bA(): Unit = println("Inner12349c#bA()") // [#12349] - not fixed by PR #9525 + private[this] override def bB(): Unit = println("Inner12349c#bB()") // weaker access privileges override def c1(): Unit = println("Inner12349c#c1()") // overrides nothing (invisible) protected override def c2(): Unit = println("Inner12349c#c2()") // weaker access privileges @@ -34,8 +36,10 @@ package pkg { private[t12349c] override def c5(): Unit = println("Inner12349c#c5()") // weaker access privileges protected[pkg] override def c6(): Unit = println("Inner12349c#c6()") // weaker access privileges private[pkg] override def c7(): Unit = println("Inner12349c#c7()") // weaker access privileges - protected[this] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges - private[this] override def c9(): Unit = println("Inner12349c#c9()") // overrides nothing (invisible) + protected[Inner12349c] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + private[Inner12349c] override def c9(): Unit = println("Inner12349c#c9()") // weaker access privileges + protected[this] override def cA(): Unit = println("Inner12349c#cA()") // weaker access privileges + private[this] override def cB(): Unit = println("Inner12349c#cB()") // weaker access privileges override def d1(): Unit = println("Inner12349c#d1()") // overrides nothing protected override def d2(): Unit = println("Inner12349c#d2()") // overrides nothing @@ -44,8 +48,10 @@ package pkg { private[t12349c] override def d5(): Unit = println("Inner12349c#d5()") // overrides nothing protected[pkg] override def d6(): Unit = println("Inner12349c#d6()") // overrides nothing private[pkg] override def d7(): Unit = println("Inner12349c#d7()") // overrides nothing - protected[this] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing - private[this] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + protected[Inner12349c] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing + private[Inner12349c] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + protected[this] override def dA(): Unit = println("Inner12349c#dA()") // overrides nothing + private[this] override def dB(): Unit = println("Inner12349c#dB()") // overrides nothing } } diff --git a/test/files/neg/t12494.check b/test/files/neg/t12494.check new file mode 100644 index 000000000000..b408a1af431e --- /dev/null +++ b/test/files/neg/t12494.check @@ -0,0 +1,164 @@ +[running phase parser on t12494.scala] +[running phase namer on t12494.scala] +[running phase packageobjects on t12494.scala] +[running phase typer on t12494.scala] +[running phase superaccessors on t12494.scala] +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / Import(value ) +[log superaccessors] [context] ++ t12494.scala / EmptyTree +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / Ident() +[log superaccessors] [context] ++ t12494.scala / Ident() +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term m +[log superaccessors] [context] ++ t12494.scala / term m +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term Y +[log superaccessors] [context] ++ t12494.scala / term Y +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term n +[log superaccessors] [context] ++ t12494.scala / term n +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / type C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / type X +[log superaccessors] [context] ++ t12494.scala / type X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term x +[log superaccessors] [context] ++ t12494.scala / term x +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / term X +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term y +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / term C +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / type C2 +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] [context] ++ t12494.scala / term test +[log superaccessors] In trait Base, renaming g -> Base$$g +[log superaccessors] Expanded 'g' to 'Base$$g' in trait Base +[log superaccessors] In trait Base, renaming h -> Base$$h +[log superaccessors] Expanded 'h' to 'Base$$h' in trait Base +[log superaccessors] In trait Base, renaming p -> Base$$p +[log superaccessors] Expanded 'p' to 'Base$$p' in trait Base +[log superaccessors] [context] ++ t12494.scala / type Base +[log superaccessors] [context] ++ t12494.scala / type Base +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term Base +[log superaccessors] [context] ++ t12494.scala / term Base +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / type Child +[log superaccessors] [context] ++ t12494.scala / type Child +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / Template(value ) +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term f +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term g +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term h +[log superaccessors] [context] ++ t12494.scala / term p +[log superaccessors] [context] ++ t12494.scala / term p +[running phase extmethods on t12494.scala] +[running phase pickler on t12494.scala] +[running phase refchecks on t12494.scala] +t12494.scala:9: error: weaker access privileges in overriding + protected[trait C] def f: scala.this.Int (defined in trait C) + override should at least be protected[C]; + found : scala.this.Int + required: scala.this.Int + protected[C] def f: Int = 42 // no, limitation + ^ +t12494.scala:28: error: weaker access privileges in overriding + protected[trait C] def f: scala.this.Int (defined in trait C) + override should at least be protected[C]; + found : scala.this.Int + required: scala.this.Int + protected[C] def f: Int = 42 // no + ^ +t12494.scala:47: error: class Child needs to be abstract. +Missing implementations for 3 members of trait Base. + private[trait Base] def g: scala.this.Int = ??? + private[trait Base] def h: scala.this.Int = ??? + private[trait Base] def p: scala.this.Int = ??? + + class Child extends Base { + ^ +t12494.scala:50: error: method g overrides nothing + override private[Base] def g: Int = 42 // ok, companion + ^ +t12494.scala:51: error: method h overrides nothing + override protected[Base] def h: Int = 42 // ok, private[C] widens to protected[C] + ^ +t12494.scala:52: error: method p overrides nothing + override protected def p: Int = 42 // error, protected only overrides protected + ^ +6 errors diff --git a/test/files/neg/t12494.scala b/test/files/neg/t12494.scala new file mode 100644 index 000000000000..4d2f27e99b85 --- /dev/null +++ b/test/files/neg/t12494.scala @@ -0,0 +1,54 @@ +// scalac: -Ylog:superaccessors -Ydebug +object X { + def m: Int = { + trait C { + protected[C] def f: Int + } + object C { + class C2 extends C { + protected[C] def f: Int = 42 // no, limitation + def test = f + } + } + new C.C2().test + } +} +object Y { + def n: Int = { + trait C { + protected[C] def f: Int + } + class X { private def x = 17 } + locally { + object X { + val y = 27 + } + object C { + class C2 extends C { + protected[C] def f: Int = 42 // no + def test = f + X.y + } + } + new C.C2().test + } + } +} + +// other combinations +// mangling qualified privates says: +// Expanded 'g' to 'Base$$g' in trait Base +trait Base { + protected[Base] def f: Int + private[Base] def g: Int + private[Base] def h: Int + private[Base] def p: Int +} +object Base { + class Child extends Base { + override protected[Base] def f: Int = 42 // ok, companion + // was: overrides nothing (because of name mangling) + override private[Base] def g: Int = 42 // ok, companion + override protected[Base] def h: Int = 42 // ok, private[C] widens to protected[C] + override protected def p: Int = 42 // error, protected only overrides protected + } +} diff --git a/test/files/neg/t4762.check b/test/files/neg/t4762.check index aa7bdcec39eb..4088131de277 100644 --- a/test/files/neg/t4762.check +++ b/test/files/neg/t4762.check @@ -1,7 +1,7 @@ -t4762.scala:17: warning: private[this] value x in class B shadows mutable x inherited from class A. Changes to x will not be visible within class B - you may want to give them distinct names. +t4762.scala:17: warning: private[this] value x in class B shadows mutable x inherited from class A. Changes to x will not be visible within class B; you may want to give them distinct names. /* (99,99) */ (this.x, this.y), ^ -t4762.scala:50: warning: private[this] value x in class Derived shadows mutable x inherited from class Base. Changes to x will not be visible within class Derived - you may want to give them distinct names. +t4762.scala:50: warning: private[this] value x in class Derived shadows mutable x inherited from class Base. Changes to x will not be visible within class Derived; you may want to give them distinct names. class Derived( x : Int ) extends Base( x ) { override def toString = x.toString } ^ t4762.scala:13: error: weaker access privileges in overriding diff --git a/test/files/neg/t8525.check b/test/files/neg/t8525.check index ca69a56c4ec3..667cccc69646 100644 --- a/test/files/neg/t8525.check +++ b/test/files/neg/t8525.check @@ -4,7 +4,7 @@ t8525.scala:9: warning: adapted the argument list to the expected 2-tuple: add a after adaptation: X.f((3, 4): (Int, Int)) def g = f(3, 4) // adapted ^ -t8525.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X - you may want to give them distinct names. +t8525.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X; you may want to give them distinct names. override def toString = name // shadowing mutable var name ^ t8525.scala:10: warning: side-effecting nullary methods are discouraged: suggest defining as `def u()` instead diff --git a/test/files/neg/t8610.check b/test/files/neg/t8610.check index 3dacf74ff7f9..e640853b67ea 100644 --- a/test/files/neg/t8610.check +++ b/test/files/neg/t8610.check @@ -7,7 +7,7 @@ t8610.scala:9: warning: adapted the argument list to the expected 2-tuple: add a after adaptation: X.f((3, 4): (Int, Int)) def g = f(3, 4) // adapted ^ -t8610.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X - you may want to give them distinct names. +t8610.scala:11: warning: private[this] value name in class X shadows mutable name inherited from class Named. Changes to name will not be visible within class X; you may want to give them distinct names. override def toString = name // shadowing mutable var name ^ t8610.scala:10: warning: side-effecting nullary methods are discouraged: suggest defining as `def u()` instead diff --git a/test/files/run/t12494.scala b/test/files/run/t12494.scala new file mode 100644 index 000000000000..9994a2744e9c --- /dev/null +++ b/test/files/run/t12494.scala @@ -0,0 +1,24 @@ + +trait Base { + protected[Base] def f: Int +} +object Base { + class Child extends Base { + protected[Base] def f: Int = 42 + def test = f + } +} + +object Test extends App { + assert(new Base.Child().test == 42) +} + +/* +was: +t12494.scala:7: error: weaker access privileges in overriding +protected[trait Base] def f: Int (defined in trait Base) + override should at least be protected[Base] + protected[Base] def f: Int = 42 + ^ +1 error +*/ From e6c8da2c21dbef54fc5226ed5230dcfeeee9e8da Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 23 Nov 2022 14:28:01 -0800 Subject: [PATCH 076/261] Partest --branch --- project/PartestUtil.scala | 3 +- .../tools/partest/nest/AbstractRunner.scala | 59 +++++++++++++++++-- .../scala/tools/partest/nest/RunnerSpec.scala | 1 + 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/project/PartestUtil.scala b/project/PartestUtil.scala index a70402ae7cc9..79dceaf4700b 100644 --- a/project/PartestUtil.scala +++ b/project/PartestUtil.scala @@ -36,7 +36,8 @@ object PartestUtil { val knownUnaryOptions = List( "--pos", "--neg", "--run", "--jvm", "--res", "--ant", "--scalap", "--specialized", "--instrumented", "--presentation", "--failed", "--update-check", "--no-exec", - "--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--realeasy", "--version", "--help") + "--show-diff", "--show-log", "--verbose", "--terse", "--debug", "--realeasy", "--branch", "--version", + "--help") val srcPathOption = "--srcpath" val compilerPathOption = "--compilerpath" val grepOption = "--grep" diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index c3e3ec76465c..829c447f8a85 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -35,6 +35,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour val verbose: Boolean = config.optVerbose val terse: Boolean = config.optTerse val realeasy: Boolean = config.optDev + val testBranch: Boolean = config.optBranch protected val printSummary = true protected val partestCmd = "partest" @@ -207,7 +208,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour } else { val norm = Function.chain(Seq(testIdentToTestPath, checkFileToTestFile, testFileToTestDir, testDirToTestFile)) - val (individualTests, invalid) = config.parsed.residualArgs map (p => norm(Path(p))) partition denotesTestPath + val (individualTests, invalid) = config.parsed.residualArgs.map(p => norm(Path(p))).partition(denotesTestPath) if (invalid.nonEmpty) { if (verbose) invalid foreach (p => echoWarning(s"Discarding invalid test path " + p)) @@ -232,9 +233,58 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour paths.sortBy(_.toString) } + // tests touched on this branch + val branchedTests: List[Path] = if (!testBranch) Nil else { + import scala.util.chaining._ + //* issue/12494 8dfd7f015d [upstream/2.13.x: ahead 1] Allow companion access boundary + //git rev-parse --abbrev-ref HEAD + //git branch -vv --list issue/1234 + //git diff --name-only upstream/2.13.x + val parseVerbose = raw"\* \S+ \S+ \[([^:]+): .*\] .*".r + def parseTracking(line: String) = line match { + case parseVerbose(tracking) => tracking.tap(ref => echo(s"Tracking $ref")) + case _ => "upstream/2.13.x".tap(default => echoWarning(s"Tracking default $default, failed to understand '$line'")) + } + def isTestFiles(path: String) = path.startsWith("test/files/") + val maybeFiles = + for { + current <- runGit("rev-parse --abbrev-ref HEAD")(_.head).tap(_.foreach(b => echo(s"Testing on branch $b"))) + tracking <- runGit(s"branch -vv --list $current")(lines => parseTracking(lines.head)) + files <- runGit(s"diff --name-only $tracking")(lines => lines.filter(isTestFiles).toList) + } + yield files + //test/files/neg/t12349.check + //test/files/neg/t12349/t12349a.java + //test/files/neg/t12349/t12349b.scala + //test/files/neg/t12349/t12349c.scala + //test/files/neg/t12494.check + //test/files/neg/t12494.scala + maybeFiles.getOrElse(Nil).flatMap { s => + val path = Path(s) + val segs = path.segments + if (segs.length < 4 || !standardKinds.contains(segs(2))) Nil + else if (segs.length > 4) { + val prefix = Path(path.segments.take(4).mkString("/")) + List(pathSettings.testParent / prefix) + } + else { + // p.check -> p.scala or p + val norm = + if (!path.hasExtension("scala") && !path.isDirectory) { + val asDir = Path(path.path.stripSuffix(s".${path.extension}")) + if (asDir.exists) asDir + else asDir.addExtension("scala") + } + else path + List(pathSettings.testParent / norm) + } + } + .distinct + } + val isRerun = config.optFailed val rerunTests = if (isRerun) testKinds.failedTests else Nil - def miscTests = individualTests ++ greppedTests ++ rerunTests + def miscTests = individualTests ++ greppedTests ++ branchedTests ++ rerunTests val givenKinds = standardKinds filter config.parsed.isSet val kinds = ( @@ -253,8 +303,9 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour if (rerunTests.isEmpty) "" else "previously failed tests", if (kindsTests.isEmpty) "" else s"${kinds.size} named test categories", if (greppedTests.isEmpty) "" else s"${greppedTests.size} tests matching '$grepExpr'", - if (individualTests.isEmpty) "" else "specified tests" - ) filterNot (_ == "") mkString ", " + if (branchedTests.isEmpty) "" else s"${branchedTests.size} tests modified on this branch", + if (individualTests.isEmpty) "" else "specified tests", + ).filterNot(_.isEmpty).mkString(", ") } val allTests: Array[Path] = distinctBy(miscTests ++ kindsTests)(_.toCanonical).sortBy(_.toString).toArray diff --git a/src/partest/scala/tools/partest/nest/RunnerSpec.scala b/src/partest/scala/tools/partest/nest/RunnerSpec.scala index e6a765f17e28..37dc5ca433de 100644 --- a/src/partest/scala/tools/partest/nest/RunnerSpec.scala +++ b/src/partest/scala/tools/partest/nest/RunnerSpec.scala @@ -57,6 +57,7 @@ trait RunnerSpec extends Spec with Meta.StdOpts with Interpolation { heading("Other options:") val optDev = "realeasy" / "real easy way to test --release 8 and check uncommitted checks" --? + val optBranch = "branch" / "test changes on this branch" --? val optVersion = "version" / "show Scala version and exit" --? val optHelp = "help" / "show this page and exit" --? From 0fc86bac0c1cccab846f758c808469035267e75a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 23 Nov 2022 16:10:52 -0800 Subject: [PATCH 077/261] Leverage this.type --- .../scala/tools/nsc/transform/Fields.scala | 4 ++-- .../tools/nsc/typechecker/PatternTypers.scala | 4 ++-- src/library/scala/MatchError.scala | 10 +++++----- .../scala/collection/mutable/ArrayBuffer.scala | 3 ++- src/reflect/scala/reflect/api/Printers.scala | 14 ++++++++------ .../scala/reflect/internal/AnnotationInfos.scala | 3 +-- src/reflect/scala/reflect/internal/Scopes.scala | 6 +++--- src/reflect/scala/reflect/internal/Symbols.scala | 2 +- src/reflect/scala/reflect/internal/Trees.scala | 2 +- .../scala/reflect/internal/transform/UnCurry.scala | 2 +- .../scala/reflect/runtime/SymbolLoaders.scala | 2 +- .../scala/reflect/runtime/SynchronizedOps.scala | 2 +- 12 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index c4f1ba58e071..1ffef3ed8e6b 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -364,8 +364,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor if (newDecls.nonEmpty) { val allDecls = newScope - origDecls foreach allDecls.enter - newDecls foreach allDecls.enter + origDecls.foreach(allDecls.enter(_)) + newDecls .foreach(allDecls.enter(_)) ClassInfoType(parents, allDecls, clazz) } else tp diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 176867663f40..ff65fd4a5bd3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -281,7 +281,7 @@ trait PatternTypers { // use "tree" for the context, not context.tree: don't make another CaseDef context, // as instantiateTypeVar's bounds would end up there val ctorContext = context.makeNewScope(tree, context.owner) - freeVars foreach ctorContext.scope.enter + freeVars.foreach(ctorContext.scope.enter(_)) newTyper(ctorContext).infer.inferConstructorInstance(tree1, undetparams, ptSafe) // simplify types without losing safety, @@ -326,7 +326,7 @@ trait PatternTypers { } val GenPolyType(freeVars, unappFormal) = freshArgType(unapplyType.skolemizeExistential(context.owner, tree)) val unapplyContext = context.makeNewScope(tree, context.owner) - freeVars.foreach(unapplyContext.scope.enter) + freeVars.foreach(unapplyContext.scope.enter(_)) val pattp = newTyper(unapplyContext).infer.inferTypedPattern(tree, unappFormal, pt, canRemedy = canRemedy, isUnapply = true) // turn any unresolved type variables in freevars into existential skolems val skolems = freeVars.map(fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) diff --git a/src/library/scala/MatchError.scala b/src/library/scala/MatchError.scala index 49a72b4ef1fa..bc94f2cbabdb 100644 --- a/src/library/scala/MatchError.scala +++ b/src/library/scala/MatchError.scala @@ -23,11 +23,11 @@ final class MatchError(@transient obj: Any) extends RuntimeException { private[this] lazy val objString = { def ofClass = "of class " + obj.getClass.getName if (obj == null) "null" - else try { - obj.toString() + " (" + ofClass + ")" - } catch { - case _: Throwable => "an instance " + ofClass - } + else + try s"$obj ($ofClass)" + catch { + case _: Throwable => "an instance " + ofClass + } } @throws[java.io.ObjectStreamException] diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala index 3d0f7385d72b..e9dfec3455cd 100644 --- a/src/library/scala/collection/mutable/ArrayBuffer.scala +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -296,7 +296,8 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] { if (k >= 0) { // Avoid reallocation of buffer if length is known val array = ensureSize(emptyArray, 0, k) // don't duplicate sizing logic, and check VM array size limit - IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + val actual = IterableOnce.copyElemsToArray(coll, array.asInstanceOf[Array[Any]]) + if (actual != k) throw new IllegalStateException(s"Copied $actual of $k") new ArrayBuffer[B](array, k) } else new ArrayBuffer[B] ++= coll diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index 28ad8e05718d..fef8321b6a3f 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -188,12 +188,14 @@ trait Printers { self: Universe => val buffer = new StringWriter() val writer = new PrintWriter(buffer) val printer = mkPrinter(writer) - printTypes.value.map(printTypes => if (printTypes) printer.withTypes else printer.withoutTypes) - printIds.value.map(printIds => if (printIds) printer.withIds else printer.withoutIds) - printOwners.value.map(printOwners => if (printOwners) printer.withOwners else printer.withoutOwners) - printKinds.value.map(printKinds => if (printKinds) printer.withKinds else printer.withoutKinds) - printMirrors.value.map(printMirrors => if (printMirrors) printer.withMirrors else printer.withoutMirrors) - printPositions.value.map(printPositions => if (printPositions) printer.withPositions else printer.withoutPositions) + + printTypes.value.foreach(if (_) printer.withTypes else printer.withoutTypes) + printIds.value.foreach(if (_) printer.withIds else printer.withoutIds) + printOwners.value.foreach(if (_) printer.withOwners else printer.withoutOwners) + printKinds.value.foreach(if (_) printer.withKinds else printer.withoutKinds) + printMirrors.value.foreach(if (_) printer.withMirrors else printer.withoutMirrors) + printPositions.value.foreach(if (_) printer.withPositions else printer.withoutPositions) + printer.print(what) writer.flush() buffer.toString diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 5d67ec388214..c8cbfd5b9a50 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -140,8 +140,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => def original = orig def setOriginal(t: Tree): this.type = { orig = t - this setPos t.pos - this + setPos(t.pos) } override def toString = completeAnnotationToString(this) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index 97fe98712ab1..92e03eb4314b 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -153,12 +153,12 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** enter a symbol */ - def enter[T <: Symbol](sym: T): T = { + def enter[T <: Symbol](sym: T): sym.type = { enterEntry(newScopeEntry(sym, this)) sym } - final def enterBefore(sym: Symbol, next: ScopeEntry): Symbol = { + final def enterBefore(sym: Symbol, next: ScopeEntry): sym.type = { assert(this != EmptyScope, sym) require(sym.name.hashCode() == next.sym.name.hashCode(), (sym, next.sym)) require(sym != next.sym, (sym, next.sym)) @@ -529,7 +529,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => def newScopeWith(elems: Symbol*): Scope = { val startTime = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null val scope = newScope - elems foreach scope.enter + elems.foreach(scope.enter(_)) if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime) scope } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a41fbe6c9be4..da84afca58cf 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3873,7 +3873,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[scala] final def argsDependOnPrefix(sym: Symbol): Boolean = { val tt = sym.owner.thisType - @annotation.tailrec + @tailrec def loop(mt: Type): Boolean = { mt match { case MethodType(params, restpe) => params.exists(_.info.dealias.exists(_ == tt)) || loop(restpe) diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 0217da48700c..10fcefe4978f 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1452,7 +1452,7 @@ trait Trees extends api.Trees { super.setType(NoType) override def canHaveAttrs = false - override def setPos(pos: Position) = { requireLegal(pos, NoPosition, "pos"); this } + override def setPos(pos: Position): this.type = { requireLegal(pos, NoPosition, "pos"); this } override def pos_=(pos: Position) = setPos(pos) override def setType(t: Type) = { requireLegal(t, NoType, "tpe"); this } override def tpe_=(t: Type) = setType(t) diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index d8f50f9d3a9f..a6cfbed7ea8d 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -107,7 +107,7 @@ trait UnCurry { if ((parents1 eq parents) && varargOverloads.isEmpty) tp else { val newDecls = decls.cloneScope - varargOverloads.foreach(newDecls.enter) + varargOverloads.foreach(newDecls.enter(_)) ClassInfoType(parents1, newDecls, clazz) } // @MAT normalize in decls?? diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 7d108b355a24..4dd9976f1f51 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -111,7 +111,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => // materializing multiple copies of the same symbol in PackageScope is a very popular bug // this override does its best to guard against it - override def enter[T <: Symbol](sym: T): T = { + override def enter[T <: Symbol](sym: T): sym.type = { // workaround for scala/bug#7728 if (isCompilerUniverse) super.enter(sym) else { diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index 3ce1330008f5..2922f13798da 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -59,7 +59,7 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable def syncLockSynchronized[T](body: => T): T = if (isCompilerUniverse) body else syncLock.synchronized { body } override def isEmpty: Boolean = syncLockSynchronized { super.isEmpty } override def size: Int = syncLockSynchronized { super.size } - override def enter[T <: Symbol](sym: T): T = syncLockSynchronized { super.enter(sym) } + override def enter[T <: Symbol](sym: T): sym.type = syncLockSynchronized { super.enter(sym) } override def rehash(sym: Symbol, newname: Name) = syncLockSynchronized { super.rehash(sym, newname) } override def unlink(e: ScopeEntry) = syncLockSynchronized { super.unlink(e) } override def unlink(sym: Symbol) = syncLockSynchronized { super.unlink(sym) } From 6b60b69bc003988fdaf46d1b1a305c0c381a8b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E5=A2=83=E8=BF=B7=E7=A6=BB?= Date: Fri, 23 Sep 2022 13:11:18 +0800 Subject: [PATCH 078/261] Fix overload resolution with function literal for SAM parameter Drop OverloadedArgProto in inferSamType --- .../scala/tools/nsc/typechecker/Typers.scala | 100 +++++++++--------- test/files/run/splain.check | 4 - test/files/run/splain.scala | 2 +- test/files/run/t12560.check | 5 + test/files/run/t12560.scala | 22 ++++ 5 files changed, 79 insertions(+), 54 deletions(-) create mode 100644 test/files/run/t12560.check create mode 100644 test/files/run/t12560.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e1eed2b88345..df204a5bcd77 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2908,69 +2908,71 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * function type is a built-in FunctionN or some SAM type * */ - def inferSamType(fun: Tree, pt: Type, mode: Mode): Boolean = fun match { - case fun@Function(vparams, _) if !isFunctionType(pt) => - // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again??? - // seems like overloading complicates things? - val sam = samOfProto(pt) - - if (!samMatchesFunctionBasedOnArity(sam, vparams)) false - else { - def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && { - val samMethType = pt memberInfo sam - fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType) - } + def inferSamType(fun: Tree, pt: Type, mode: Mode): Boolean = + if (pt.isInstanceOf[OverloadedArgProto]) inferSamType(fun, pt.underlying, mode) // scala/bug#12560 + else fun match { + case fun@Function(vparams, _) if !isFunctionType(pt) => + // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again??? + // seems like overloading complicates things? + val sam = samOfProto(pt) + + if (!samMatchesFunctionBasedOnArity(sam, vparams)) false + else { + def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && { + val samMethType = pt memberInfo sam + fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType) + } - val samTp = - if (!sam.exists) NoType - else if (fullyDefinedMeetsExpectedFunTp(pt)) pt - else try { - val ptFullyDefined = instantiateSamFromFunction(fun.tpe, pt, sam) - if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) { - debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}") - ptFullyDefined - } else { - debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)") - NoType + val samTp = + if (!sam.exists) NoType + else if (fullyDefinedMeetsExpectedFunTp(pt)) pt + else try { + val ptFullyDefined = instantiateSamFromFunction(fun.tpe, pt, sam) + if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) { + debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}") + ptFullyDefined + } else { + debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)") + NoType + } + } catch { + case e@(_: NoInstance | _: TypeError) => + debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e") + NoType } - } catch { - case e@(_: NoInstance | _: TypeError) => - debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e") - NoType - } - if (samTp eq NoType) false - else { - /* Make a synthetic class symbol to represent the synthetic class that + if (samTp eq NoType) false + else { + /* Make a synthetic class symbol to represent the synthetic class that * will be spun up by LMF for this function. This is necessary because * it's possible that the SAM method might need bridges, and they have * to go somewhere. Erasure knows to compute bridges for these classes * just as if they were real templates extending the SAM type. */ - val synthCls = fun.symbol.owner.newClassWithInfo( - name = tpnme.ANON_CLASS_NAME, - parents = ObjectTpe :: samTp :: Nil, - scope = newScope, - pos = sam.pos, - newFlags = SYNTHETIC | ARTIFACT - ) + val synthCls = fun.symbol.owner.newClassWithInfo( + name = tpnme.ANON_CLASS_NAME, + parents = ObjectTpe :: samTp :: Nil, + scope = newScope, + pos = sam.pos, + newFlags = SYNTHETIC | ARTIFACT + ) - synthCls.info.decls.enter { - val newFlags = (sam.flags & ~DEFERRED) | SYNTHETIC - sam.cloneSymbol(synthCls, newFlags).setInfo(samTp memberInfo sam) - } + synthCls.info.decls.enter { + val newFlags = (sam.flags & ~DEFERRED) | SYNTHETIC + sam.cloneSymbol(synthCls, newFlags).setInfo(samTp memberInfo sam) + } - fun.setType(samTp) + fun.setType(samTp) - /* Arguably I should do `fun.setSymbol(samCls)` rather than leaning + /* Arguably I should do `fun.setSymbol(samCls)` rather than leaning * on an attachment, but doing that confounds lambdalift's free var * analysis in a way which does not seem to be trivially reparable. */ - fun.updateAttachment(SAMFunction(samTp, sam, synthCls)) + fun.updateAttachment(SAMFunction(samTp, sam, synthCls)) - true + true + } } - } - case _ => false - } + case _ => false + } /** * Deconstruct an expected function-ish type `pt` into `numVparams` argument prototypes and a result prototype. diff --git a/test/files/run/splain.check b/test/files/run/splain.check index b3494546631b..9dbb8db96b7c 100644 --- a/test/files/run/splain.check +++ b/test/files/run/splain.check @@ -11,10 +11,6 @@ newSource1.scala:6: error: type mismatch; FoundReq.L|FoundReq.R f(new L) ^ -newSource1.scala:5: error: type mismatch; - () => scala.Unit|Runnable - f(3.0, () => println("doesn't work")) - ^ newSource1.scala:7: error: implicit error; !I e: Bounds.F[Bounds.Arg] implicitly[F[Arg]] diff --git a/test/files/run/splain.scala b/test/files/run/splain.scala index fbbbe2acec7e..c291c3338bbd 100644 --- a/test/files/run/splain.scala +++ b/test/files/run/splain.scala @@ -39,7 +39,7 @@ object FoundReq extends App { def f(x: AnyVal, f: Runnable) = 1 def f(x: Double, f: Runnable) = 2 - f(3.0, () => println("doesn't work")) + f(3.0, () => println("work")) } """ diff --git a/test/files/run/t12560.check b/test/files/run/t12560.check new file mode 100644 index 000000000000..7ac20ecffd35 --- /dev/null +++ b/test/files/run/t12560.check @@ -0,0 +1,5 @@ +2 +2 +2 +1 +1 diff --git a/test/files/run/t12560.scala b/test/files/run/t12560.scala new file mode 100644 index 000000000000..e4e913fd3eda --- /dev/null +++ b/test/files/run/t12560.scala @@ -0,0 +1,22 @@ +object Test extends App { + + def f(x: AnyVal, f: Runnable) = 1 + def f(x: Double, f: Runnable) = 2 + + val ret1 = f(3.0, () => println("work")) // 2 match Double + println(ret1) + + val ret2 = f(3, () => println("work")) // 2 match Double + println(ret2) + + + val ret3 = f('a', () => println("work")) // 2 match Double, char ==> Int => Double + println(ret3) + + val ret4 = f(false, () => println("work")) // 1 match AnyVal + println(ret4) + + val ret5 = f((), () => println("work")) // 1 match AnyVal + println(ret5) + +} \ No newline at end of file From 22fd88d03c0b2d2bf159c263ce3435b0cf061f85 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 6 Nov 2022 14:12:14 -0800 Subject: [PATCH 079/261] Prefer knownSize to isEmpty in IterableOnceOps --- project/MimaFilters.scala | 3 + .../scala/collection/IterableOnce.scala | 1300 +++++++++-------- .../junit/scala/collection/IterableTest.scala | 265 +++- 3 files changed, 851 insertions(+), 717 deletions(-) diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index 4e801b2cdf6c..b2146dd14066 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -38,6 +38,9 @@ object MimaFilters extends AutoPlugin { ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.impl.FutureConvertersImpl#P.accept"), ProblemFilters.exclude[IncompatibleMethTypeProblem]("scala.concurrent.impl.FutureConvertersImpl#P.andThen"), + + // private class used by methods + ProblemFilters.exclude[MissingClassProblem]("scala.collection.IterableOnceOps$Maximized"), ) override val buildSettings = Seq( diff --git a/src/library/scala/collection/IterableOnce.scala b/src/library/scala/collection/IterableOnce.scala index 2f5fc3ad77ec..52024c49b660 100644 --- a/src/library/scala/collection/IterableOnce.scala +++ b/src/library/scala/collection/IterableOnce.scala @@ -19,6 +19,7 @@ import scala.collection.mutable.StringBuilder import scala.language.implicitConversions import scala.math.{Numeric, Ordering} import scala.reflect.ClassTag +import scala.runtime.AbstractFunction2 /** * A template trait for collections which can be traversed either once only @@ -76,8 +77,8 @@ trait IterableOnce[+A] extends Any { } /** @return The number of elements in this $coll, if it can be cheaply computed, - * -1 otherwise. Cheaply usually means: Not requiring a collection traversal. - */ + * -1 otherwise. Cheaply usually means: Not requiring a collection traversal. + */ def knownSize: Int = -1 } @@ -318,184 +319,184 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => /////////////////////////////////////////////////////////////// Abstract methods that must be implemented /** Produces a $coll containing cumulative results of applying the - * operator going left to right, including the initial value. - * - * $willNotTerminateInf - * $orderDependent - * - * @tparam B the type of the elements in the resulting collection - * @param z the initial value - * @param op the binary operator applied to the intermediate result and the element - * @return collection with intermediate results - */ + * operator going left to right, including the initial value. + * + * $willNotTerminateInf + * $orderDependent + * + * @tparam B the type of the elements in the resulting collection + * @param z the initial value + * @param op the binary operator applied to the intermediate result and the element + * @return collection with intermediate results + */ def scanLeft[B](z: B)(op: (B, A) => B): CC[B] /** Selects all elements of this $coll which satisfy a predicate. - * - * @param p the predicate used to test elements. - * @return a new $coll consisting of all elements of this $coll that satisfy the given - * predicate `p`. The order of the elements is preserved. - */ + * + * @param p the predicate used to test elements. + * @return a new $coll consisting of all elements of this $coll that satisfy the given + * predicate `p`. The order of the elements is preserved. + */ def filter(p: A => Boolean): C /** Selects all elements of this $coll which do not satisfy a predicate. - * - * @param pred the predicate used to test elements. - * @return a new $coll consisting of all elements of this $coll that do not satisfy the given - * predicate `pred`. Their order may not be preserved. - */ + * + * @param pred the predicate used to test elements. + * @return a new $coll consisting of all elements of this $coll that do not satisfy the given + * predicate `pred`. Their order may not be preserved. + */ def filterNot(pred: A => Boolean): C /** Selects the first ''n'' elements. - * $orderDependent - * @param n the number of elements to take from this $coll. - * @return a $coll consisting only of the first `n` elements of this $coll, - * or else the whole $coll, if it has less than `n` elements. - * If `n` is negative, returns an empty $coll. - */ + * $orderDependent + * @param n the number of elements to take from this $coll. + * @return a $coll consisting only of the first `n` elements of this $coll, + * or else the whole $coll, if it has less than `n` elements. + * If `n` is negative, returns an empty $coll. + */ def take(n: Int): C /** Takes longest prefix of elements that satisfy a predicate. - * $orderDependent - * @param p The predicate used to test elements. - * @return the longest prefix of this $coll whose elements all satisfy - * the predicate `p`. - */ + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest prefix of this $coll whose elements all satisfy + * the predicate `p`. + */ def takeWhile(p: A => Boolean): C /** Selects all elements except first ''n'' ones. - * $orderDependent - * @param n the number of elements to drop from this $coll. - * @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the - * empty $coll, if this $coll has less than `n` elements. - * If `n` is negative, don't drop any elements. - */ + * $orderDependent + * @param n the number of elements to drop from this $coll. + * @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the + * empty $coll, if this $coll has less than `n` elements. + * If `n` is negative, don't drop any elements. + */ def drop(n: Int): C /** Drops longest prefix of elements that satisfy a predicate. - * $orderDependent - * @param p The predicate used to test elements. - * @return the longest suffix of this $coll whose first element - * does not satisfy the predicate `p`. - */ + * $orderDependent + * @param p The predicate used to test elements. + * @return the longest suffix of this $coll whose first element + * does not satisfy the predicate `p`. + */ def dropWhile(p: A => Boolean): C /** Selects an interval of elements. The returned $coll is made up - * of all elements `x` which satisfy the invariant: - * {{{ - * from <= indexOf(x) < until - * }}} - * $orderDependent - * - * @param from the lowest index to include from this $coll. - * @param until the lowest index to EXCLUDE from this $coll. - * @return a $coll containing the elements greater than or equal to - * index `from` extending up to (but not including) index `until` - * of this $coll. - */ + * of all elements `x` which satisfy the invariant: + * {{{ + * from <= indexOf(x) < until + * }}} + * $orderDependent + * + * @param from the lowest index to include from this $coll. + * @param until the lowest index to EXCLUDE from this $coll. + * @return a $coll containing the elements greater than or equal to + * index `from` extending up to (but not including) index `until` + * of this $coll. + */ def slice(from: Int, until: Int): C /** Builds a new $coll by applying a function to all elements of this $coll. - * - * @param f the function to apply to each element. - * @tparam B the element type of the returned $coll. - * @return a new $coll resulting from applying the given function - * `f` to each element of this $coll and collecting the results. - */ + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned $coll. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results. + */ def map[B](f: A => B): CC[B] /** Builds a new $coll by applying a function to all elements of this $coll - * and using the elements of the resulting collections. - * - * For example: - * - * {{{ - * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") - * }}} - * - * The type of the resulting collection is guided by the static type of $coll. This might - * cause unexpected results sometimes. For example: - * - * {{{ - * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set - * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) - * - * // lettersOf will return a Set[Char], not a Seq - * def lettersOf(words: Seq[String]) = words.toSet flatMap ((word: String) => word.toSeq) - * - * // xs will be an Iterable[Int] - * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) - * - * // ys will be a Map[Int, Int] - * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) - * }}} - * - * @param f the function to apply to each element. - * @tparam B the element type of the returned collection. - * @return a new $coll resulting from applying the given collection-valued function - * `f` to each element of this $coll and concatenating the results. - */ + * and using the elements of the resulting collections. + * + * For example: + * + * {{{ + * def getWords(lines: Seq[String]): Seq[String] = lines flatMap (line => line split "\\W+") + * }}} + * + * The type of the resulting collection is guided by the static type of $coll. This might + * cause unexpected results sometimes. For example: + * + * {{{ + * // lettersOf will return a Seq[Char] of likely repeated letters, instead of a Set + * def lettersOf(words: Seq[String]) = words flatMap (word => word.toSet) + * + * // lettersOf will return a Set[Char], not a Seq + * def lettersOf(words: Seq[String]) = words.toSet flatMap ((word: String) => word.toSeq) + * + * // xs will be an Iterable[Int] + * val xs = Map("a" -> List(11,111), "b" -> List(22,222)).flatMap(_._2) + * + * // ys will be a Map[Int, Int] + * val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(_._2) + * }}} + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given collection-valued function + * `f` to each element of this $coll and concatenating the results. + */ def flatMap[B](f: A => IterableOnce[B]): CC[B] /** Converts this $coll of iterable collections into - * a $coll formed by the elements of these iterable - * collections. - * - * The resulting collection's type will be guided by the - * type of $coll. For example: - * - * {{{ - * val xs = List( - * Set(1, 2, 3), - * Set(1, 2, 3) - * ).flatten - * // xs == List(1, 2, 3, 1, 2, 3) - * - * val ys = Set( - * List(1, 2, 3), - * List(3, 2, 1) - * ).flatten - * // ys == Set(1, 2, 3) - * }}} - * - * @tparam B the type of the elements of each iterable collection. - * @param asIterable an implicit conversion which asserts that the element - * type of this $coll is an `Iterable`. - * @return a new $coll resulting from concatenating all element ${coll}s. - */ + * a $coll formed by the elements of these iterable + * collections. + * + * The resulting collection's type will be guided by the + * type of $coll. For example: + * + * {{{ + * val xs = List( + * Set(1, 2, 3), + * Set(1, 2, 3) + * ).flatten + * // xs == List(1, 2, 3, 1, 2, 3) + * + * val ys = Set( + * List(1, 2, 3), + * List(3, 2, 1) + * ).flatten + * // ys == Set(1, 2, 3) + * }}} + * + * @tparam B the type of the elements of each iterable collection. + * @param asIterable an implicit conversion which asserts that the element + * type of this $coll is an `Iterable`. + * @return a new $coll resulting from concatenating all element ${coll}s. + */ def flatten[B](implicit asIterable: A => IterableOnce[B]): CC[B] /** Builds a new $coll by applying a partial function to all elements of this $coll - * on which the function is defined. - * - * @param pf the partial function which filters and maps the $coll. - * @tparam B the element type of the returned $coll. - * @return a new $coll resulting from applying the given partial function - * `pf` to each element on which it is defined and collecting the results. - * The order of the elements is preserved. - */ + * on which the function is defined. + * + * @param pf the partial function which filters and maps the $coll. + * @tparam B the element type of the returned $coll. + * @return a new $coll resulting from applying the given partial function + * `pf` to each element on which it is defined and collecting the results. + * The order of the elements is preserved. + */ def collect[B](pf: PartialFunction[A, B]): CC[B] /** Zips this $coll with its indices. - * - * @return A new $coll containing pairs consisting of all elements of this $coll paired with their index. - * Indices start at `0`. - * @example - * `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))` - */ + * + * @return A new $coll containing pairs consisting of all elements of this $coll paired with their index. + * Indices start at `0`. + * @example + * `List("a", "b", "c").zipWithIndex == List(("a", 0), ("b", 1), ("c", 2))` + */ def zipWithIndex: CC[(A @uncheckedVariance, Int)] /** Splits this $coll into a prefix/suffix pair according to a predicate. - * - * Note: `c span p` is equivalent to (but possibly more efficient than) - * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the - * predicate `p` does not cause any side-effects. - * $orderDependent - * - * @param p the test predicate - * @return a pair consisting of the longest prefix of this $coll whose - * elements all satisfy `p`, and the rest of this $coll. - */ + * + * Note: `c span p` is equivalent to (but possibly more efficient than) + * `(c takeWhile p, c dropWhile p)`, provided the evaluation of the + * predicate `p` does not cause any side-effects. + * $orderDependent + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of this $coll whose + * elements all satisfy `p`, and the rest of this $coll. + */ def span(p: A => Boolean): (C, C) /** Splits this $coll into a prefix/suffix pair at a given position. @@ -531,32 +532,32 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => /////////////////////////////////////////////////////////////// Concrete methods based on iterator /** Tests whether this $coll is known to have a finite size. - * All strict collections are known to have finite size. For a non-strict - * collection such as `Stream`, the predicate returns `'''true'''` if all - * elements have been computed. It returns `'''false'''` if the stream is - * not yet evaluated to the end. Non-empty Iterators usually return - * `'''false'''` even if they were created from a collection with a known - * finite size. - * - * Note: many collection methods will not work on collections of infinite sizes. - * The typical failure mode is an infinite loop. These methods always attempt a - * traversal without checking first that `hasDefiniteSize` returns `'''true'''`. - * However, checking `hasDefiniteSize` can provide an assurance that size is - * well-defined and non-termination is not a concern. - * - * @deprecated This method is deprecated in 2.13 because it does not provide any - * actionable information. As noted above, even the collection library itself - * does not use it. When there is no guarantee that a collection is finite, it - * is generally best to attempt a computation anyway and document that it will - * not terminate for infinite collections rather than backing out because this - * would prevent performing the computation on collections that are in fact - * finite even though `hasDefiniteSize` returns `false`. - * - * @see method `knownSize` for a more useful alternative - * - * @return `'''true'''` if this collection is known to have finite size, - * `'''false'''` otherwise. - */ + * All strict collections are known to have finite size. For a non-strict + * collection such as `Stream`, the predicate returns `'''true'''` if all + * elements have been computed. It returns `'''false'''` if the stream is + * not yet evaluated to the end. Non-empty Iterators usually return + * `'''false'''` even if they were created from a collection with a known + * finite size. + * + * Note: many collection methods will not work on collections of infinite sizes. + * The typical failure mode is an infinite loop. These methods always attempt a + * traversal without checking first that `hasDefiniteSize` returns `'''true'''`. + * However, checking `hasDefiniteSize` can provide an assurance that size is + * well-defined and non-termination is not a concern. + * + * @deprecated This method is deprecated in 2.13 because it does not provide any + * actionable information. As noted above, even the collection library itself + * does not use it. When there is no guarantee that a collection is finite, it + * is generally best to attempt a computation anyway and document that it will + * not terminate for infinite collections rather than backing out because this + * would prevent performing the computation on collections that are in fact + * finite even though `hasDefiniteSize` returns `false`. + * + * @see method `knownSize` for a more useful alternative + * + * @return `'''true'''` if this collection is known to have finite size, + * `'''false'''` otherwise. + */ @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") def hasDefiniteSize: Boolean = true @@ -568,21 +569,21 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def isTraversableAgain: Boolean = false /** Apply `f` to each element for its side effects - * Note: [U] parameter needed to help scalac's type inference. - */ + * Note: [U] parameter needed to help scalac's type inference. + */ def foreach[U](f: A => U): Unit = { val it = iterator while(it.hasNext) f(it.next()) } /** Tests whether a predicate holds for all elements of this $coll. - * - * $mayNotTerminateInf - * - * @param p the predicate used to test elements. - * @return `true` if this $coll is empty or the given predicate `p` - * holds for all elements of this $coll, otherwise `false`. - */ + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return `true` if this $coll is empty or the given predicate `p` + * holds for all elements of this $coll, otherwise `false`. + */ def forall(p: A => Boolean): Boolean = { var res = true val it = iterator @@ -591,12 +592,12 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Tests whether a predicate holds for at least one element of this $coll. - * - * $mayNotTerminateInf - * - * @param p the predicate used to test elements. - * @return `true` if the given predicate `p` is satisfied by at least one element of this $coll, otherwise `false` - */ + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return `true` if the given predicate `p` is satisfied by at least one element of this $coll, otherwise `false` + */ def exists(p: A => Boolean): Boolean = { var res = false val it = iterator @@ -605,12 +606,12 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Counts the number of elements in the $coll which satisfy a predicate. - * - * $willNotTerminateInf - * - * @param p the predicate used to test elements. - * @return the number of elements satisfying the predicate `p`. - */ + * + * $willNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the number of elements satisfying the predicate `p`. + */ def count(p: A => Boolean): Int = { var res = 0 val it = iterator @@ -619,14 +620,14 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Finds the first element of the $coll satisfying a predicate, if any. - * - * $mayNotTerminateInf - * $orderDependent - * - * @param p the predicate used to test elements. - * @return an option value containing the first element in the $coll - * that satisfies `p`, or `None` if none exists. - */ + * + * $mayNotTerminateInf + * $orderDependent + * + * @param p the predicate used to test elements. + * @return an option value containing the first element in the $coll + * that satisfies `p`, or `None` if none exists. + */ def find(p: A => Boolean): Option[A] = { val it = iterator while (it.hasNext) { @@ -636,15 +637,15 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => None } - // TODO 2.14+ move to IndexedSeqOps - private[this] def foldl[B](seq: IndexedSeq[A], start: Int, z: B, op: (B, A) => B): B = { + // in future, move to IndexedSeqOps + private def foldl[X >: A, B](seq: IndexedSeq[X], start: Int, z: B, op: (B, X) => B): B = { @tailrec def loop(at: Int, end: Int, acc: B): B = if (at == end) acc else loop(at + 1, end, op(acc, seq(at))) loop(start, seq.length, z) } - private[this] def foldr[B >: A](seq: IndexedSeq[A], op: (A, B) => B): B = { + private def foldr[X >: A, B >: X](seq: IndexedSeq[X], op: (X, B) => B): B = { @tailrec def loop(at: Int, acc: B): B = if (at == 0) acc else loop(at - 1, op(seq(at - 1), acc)) @@ -652,22 +653,22 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Applies a binary operator to a start value and all elements of this $coll, - * going left to right. - * - * $willNotTerminateInf - * $orderDependentFold - * - * @param z the start value. - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going left to right with the start value `z` on the left: - * `op(...op(z, x,,1,,), x,,2,,, ..., x,,n,,)` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * Returns `z` if this $coll is empty. - */ + * going left to right. + * + * $willNotTerminateInf + * $orderDependentFold + * + * @param z the start value. + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going left to right with the start value `z` on the left: + * `op(...op(z, x,,1,,), x,,2,,, ..., x,,n,,)` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * Returns `z` if this $coll is empty. + */ def foldLeft[B](z: B)(op: (B, A) => B): B = this match { - case seq: IndexedSeq[A @unchecked] => foldl(seq, 0, z, op) + case seq: IndexedSeq[A @unchecked] => foldl[A, B](seq, 0, z, op) case _ => var result = z val it = iterator @@ -678,19 +679,19 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Applies a binary operator to all elements of this $coll and a start value, - * going right to left. - * - * $willNotTerminateInf - * $orderDependentFold - * @param z the start value. - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going right to left with the start value `z` on the right: - * `op(x,,1,,, op(x,,2,,, ... op(x,,n,,, z)...))` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * Returns `z` if this $coll is empty. - */ + * going right to left. + * + * $willNotTerminateInf + * $orderDependentFold + * @param z the start value. + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going right to left with the start value `z` on the right: + * `op(x,,1,,, op(x,,2,,, ... op(x,,n,,, z)...))` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * Returns `z` if this $coll is empty. + */ def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((b, a) => op(a, b)) @deprecated("Use foldLeft instead of /:", "2.13.0") @@ -700,145 +701,164 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => @`inline` final def :\ [B](z: B)(op: (A, B) => B): B = foldRight[B](z)(op) /** Folds the elements of this $coll using the specified associative binary operator. - * The default implementation in `IterableOnce` is equivalent to `foldLeft` but may be - * overridden for more efficient traversal orders. - * - * $undefinedorder - * $willNotTerminateInf - * - * @tparam A1 a type parameter for the binary operator, a supertype of `A`. - * @param z a neutral element for the fold operation; may be added to the result - * an arbitrary number of times, and must not change the result (e.g., `Nil` for list concatenation, - * 0 for addition, or 1 for multiplication). - * @param op a binary operator that must be associative. - * @return the result of applying the fold operator `op` between all the elements and `z`, or `z` if this $coll is empty. - */ + * The default implementation in `IterableOnce` is equivalent to `foldLeft` but may be + * overridden for more efficient traversal orders. + * + * $undefinedorder + * $willNotTerminateInf + * + * @tparam A1 a type parameter for the binary operator, a supertype of `A`. + * @param z a neutral element for the fold operation; may be added to the result + * an arbitrary number of times, and must not change the result (e.g., `Nil` for list concatenation, + * 0 for addition, or 1 for multiplication). + * @param op a binary operator that must be associative. + * @return the result of applying the fold operator `op` between all the elements and `z`, or `z` if this $coll is empty. + */ def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op) /** Reduces the elements of this $coll using the specified associative binary operator. - * - * $undefinedorder - * - * @tparam B A type parameter for the binary operator, a supertype of `A`. - * @param op A binary operator that must be associative. - * @return The result of applying reduce operator `op` between all the elements if the $coll is nonempty. - * @throws UnsupportedOperationException if this $coll is empty. - */ + * + * $undefinedorder + * + * @tparam B A type parameter for the binary operator, a supertype of `A`. + * @param op A binary operator that must be associative. + * @return The result of applying reduce operator `op` between all the elements if the $coll is nonempty. + * @throws UnsupportedOperationException if this $coll is empty. + */ def reduce[B >: A](op: (B, B) => B): B = reduceLeft(op) /** Reduces the elements of this $coll, if any, using the specified - * associative binary operator. - * - * $undefinedorder - * - * @tparam B A type parameter for the binary operator, a supertype of `A`. - * @param op A binary operator that must be associative. - * @return An option value containing result of applying reduce operator `op` between all - * the elements if the collection is nonempty, and `None` otherwise. - */ + * associative binary operator. + * + * $undefinedorder + * + * @tparam B A type parameter for the binary operator, a supertype of `A`. + * @param op A binary operator that must be associative. + * @return An option value containing result of applying reduce operator `op` between all + * the elements if the collection is nonempty, and `None` otherwise. + */ def reduceOption[B >: A](op: (B, B) => B): Option[B] = reduceLeftOption(op) /** Applies a binary operator to all elements of this $coll, - * going left to right. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going left to right: - * `op( op( ... op(x,,1,,, x,,2,,) ..., x,,n-1,,), x,,n,,)` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * @throws UnsupportedOperationException if this $coll is empty. */ + * going left to right. + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going left to right: + * `op( op( ... op(x,,1,,, x,,2,,) ..., x,,n-1,,), x,,n,,)` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * @throws UnsupportedOperationException if this $coll is empty. + */ def reduceLeft[B >: A](op: (B, A) => B): B = this match { case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldl(seq, 1, seq(0), op) - case _ => - val it = iterator - if (it.isEmpty) - throw new UnsupportedOperationException("empty.reduceLeft") - - var first = true - var acc: B = null.asInstanceOf[B] - - while (it.hasNext) { - val x = it.next() - if (first) { - acc = x - first = false - } - else acc = op(acc, x) - } + case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceLeft") + case _ => reduceLeftIterator[B](throw new UnsupportedOperationException("empty.reduceLeft"))(op) + } + private final def reduceLeftIterator[B >: A](onEmpty: => B)(op: (B, A) => B): B = { + val it = iterator + if (it.hasNext) { + var acc: B = it.next() + while (it.hasNext) + acc = op(acc, it.next()) acc + } + else onEmpty } /** Applies a binary operator to all elements of this $coll, going right to left. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return the result of inserting `op` between consecutive elements of this $coll, - * going right to left: - * `op(x,,1,,, op(x,,2,,, ..., op(x,,n-1,,, x,,n,,)...))` where `x,,1,,, ..., x,,n,,` - * are the elements of this $coll. - * @throws UnsupportedOperationException if this $coll is empty. - */ + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return the result of inserting `op` between consecutive elements of this $coll, + * going right to left: + * `op(x,,1,,, op(x,,2,,, ..., op(x,,n-1,,, x,,n,,)...))` where `x,,1,,, ..., x,,n,,` + * are the elements of this $coll. + * @throws UnsupportedOperationException if this $coll is empty. + */ def reduceRight[B >: A](op: (A, B) => B): B = this match { - case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr(seq, op) + case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr[A, B](seq, op) + case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceRight") case _ => - val it = iterator - if (it.isEmpty) - throw new UnsupportedOperationException("empty.reduceRight") - - reversed.reduceLeft[B]((x, y) => op(y, x)) + try reversed.reduceLeft[B]((x, y) => op(y, x)) // reduceLeftIterator + catch { + case e: UnsupportedOperationException if e.getMessage == "empty.reduceLeft" => + throw new UnsupportedOperationException("empty.reduceRight") + } } /** Optionally applies a binary operator to all elements of this $coll, going left to right. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return an option value containing the result of `reduceLeft(op)` if this $coll is nonempty, - * `None` otherwise. - */ - def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = if (isEmpty) None else Some(reduceLeft(op)) + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return an option value containing the result of `reduceLeft(op)` if this $coll is nonempty, + * `None` otherwise. + */ + def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = + knownSize match { + case -1 => reduceLeftOptionIterator[B](op) + case 0 => None + case _ => Some(reduceLeft(op)) + } + private final def reduceLeftOptionIterator[B >: A](op: (B, A) => B): Option[B] = reduceOptionIterator[A, B](iterator)(op) + private final def reduceOptionIterator[X >: A, B >: X](it: Iterator[X])(op: (B, X) => B): Option[B] = { + if (it.hasNext) { + var acc: B = it.next() + while (it.hasNext) + acc = op(acc, it.next()) + Some(acc) + } + else None + } /** Optionally applies a binary operator to all elements of this $coll, going - * right to left. - * $willNotTerminateInf - * $orderDependentFold - * - * @param op the binary operator. - * @tparam B the result type of the binary operator. - * @return an option value containing the result of `reduceRight(op)` if this $coll is nonempty, - * `None` otherwise. - */ - def reduceRightOption[B >: A](op: (A, B) => B): Option[B] = if (isEmpty) None else Some(reduceRight(op)) + * right to left. + * $willNotTerminateInf + * $orderDependentFold + * + * @param op the binary operator. + * @tparam B the result type of the binary operator. + * @return an option value containing the result of `reduceRight(op)` if this $coll is nonempty, + * `None` otherwise. + */ + def reduceRightOption[B >: A](op: (A, B) => B): Option[B] = + knownSize match { + case -1 => reduceOptionIterator[A, B](reversed.iterator)((x, y) => op(y, x)) + case 0 => None + case _ => Some(reduceRight(op)) + } /** Tests whether the $coll is empty. - * - * Note: Implementations in subclasses that are not repeatedly iterable must take - * care not to consume any elements when `isEmpty` is called. - * - * @return `true` if the $coll contains no elements, `false` otherwise. - */ + * + * Note: The default implementation creates and discards an iterator. + * + * Note: Implementations in subclasses that are not repeatedly iterable must take + * care not to consume any elements when `isEmpty` is called. + * + * @return `true` if the $coll contains no elements, `false` otherwise. + */ def isEmpty: Boolean = !iterator.hasNext /** Tests whether the $coll is not empty. - * - * @return `true` if the $coll contains at least one element, `false` otherwise. - */ + * + * @return `true` if the $coll contains at least one element, `false` otherwise. + */ @deprecatedOverriding("nonEmpty is defined as !isEmpty; override isEmpty instead", "2.13.0") def nonEmpty: Boolean = !isEmpty /** The size of this $coll. - * - * $willNotTerminateInf - * - * @return the number of elements in this $coll. - */ - def size: Int = { + * + * $willNotTerminateInf + * + * @return the number of elements in this $coll. + */ + def size: Int = if (knownSize >= 0) knownSize else { val it = iterator @@ -846,7 +866,6 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => while (it.hasNext) { len += 1; it.next() } len } - } @deprecated("Use `dest ++= coll` instead", "2.13.0") @inline final def copyToBuffer[B >: A](dest: mutable.Buffer[B]): Unit = dest ++= this @@ -868,37 +887,37 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def copyToArray[B >: A](xs: Array[B]): Int = copyToArray(xs, 0, Int.MaxValue) /** Copy elements to an array, returning the number of elements written. - * - * Fills the given array `xs` starting at index `start` with values of this $coll. - * - * Copying will stop once either all the elements of this $coll have been copied, - * or the end of the array is reached. - * - * @param xs the array to fill. - * @param start the starting index of xs. - * @tparam B the type of the elements of the array. - * @return the number of elements written to the array - * - * @note Reuse: $consumesIterator - */ + * + * Fills the given array `xs` starting at index `start` with values of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached. + * + * @param xs the array to fill. + * @param start the starting index of xs. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ @deprecatedOverriding("This should always forward to the 3-arg version of this method", since = "2.13.4") def copyToArray[B >: A](xs: Array[B], start: Int): Int = copyToArray(xs, start, Int.MaxValue) /** Copy elements to an array, returning the number of elements written. - * - * Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll. - * - * Copying will stop once either all the elements of this $coll have been copied, - * or the end of the array is reached, or `len` elements have been copied. - * - * @param xs the array to fill. - * @param start the starting index of xs. - * @param len the maximal number of elements to copy. - * @tparam B the type of the elements of the array. - * @return the number of elements written to the array - * - * @note Reuse: $consumesIterator - */ + * + * Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll. + * + * Copying will stop once either all the elements of this $coll have been copied, + * or the end of the array is reached, or `len` elements have been copied. + * + * @param xs the array to fill. + * @param start the starting index of xs. + * @param len the maximal number of elements to copy. + * @tparam B the type of the elements of the array. + * @return the number of elements written to the array + * + * @note Reuse: $consumesIterator + */ def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = { val it = iterator var i = start @@ -911,200 +930,203 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Sums up the elements of this collection. - * - * $willNotTerminateInf - * - * @param num an implicit parameter defining a set of numeric operations - * which includes the `+` operator to be used in forming the sum. - * @tparam B the result type of the `+` operator. - * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. - */ - def sum[B >: A](implicit num: Numeric[B]): B = if (isEmpty) num.zero else reduce(num.plus) + * + * $willNotTerminateInf + * + * @param num an implicit parameter defining a set of numeric operations + * which includes the `+` operator to be used in forming the sum. + * @tparam B the result type of the `+` operator. + * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. + */ + def sum[B >: A](implicit num: Numeric[B]): B = + knownSize match { + case -1 => reduceLeftIterator[B](num.zero)(num.plus) + case 0 => num.zero + case _ => reduce(num.plus) + } /** Multiplies up the elements of this collection. - * - * $willNotTerminateInf - * - * @param num an implicit parameter defining a set of numeric operations - * which includes the `*` operator to be used in forming the product. - * @tparam B the result type of the `*` operator. - * @return the product of all elements of this $coll with respect to the `*` operator in `num`. - */ - def product[B >: A](implicit num: Numeric[B]): B = if (isEmpty) num.one else reduce(num.times) + * + * $willNotTerminateInf + * + * @param num an implicit parameter defining a set of numeric operations + * which includes the `*` operator to be used in forming the product. + * @tparam B the result type of the `*` operator. + * @return the product of all elements of this $coll with respect to the `*` operator in `num`. + */ + def product[B >: A](implicit num: Numeric[B]): B = + knownSize match { + case -1 => reduceLeftIterator[B](num.one)(num.times) + case 0 => num.one + case _ => reduce(num.times) + } /** Finds the smallest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the smallest element of this $coll with respect to the ordering `ord`. - * - */ - def min[B >: A](implicit ord: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.min") - reduceLeft(ord.min) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the smallest element of this $coll with respect to the ordering `ord`. + * + */ + def min[B >: A](implicit ord: Ordering[B]): A = + knownSize match { + case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.min"))(ord.min) + case 0 => throw new UnsupportedOperationException("empty.min") + case _ => reduceLeft(ord.min) + } /** Finds the smallest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @return an option value containing the smallest element of this $coll - * with respect to the ordering `ord`. - */ - def minOption[B >: A](implicit ord: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(min(ord)) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @return an option value containing the smallest element of this $coll + * with respect to the ordering `ord`. + */ + def minOption[B >: A](implicit ord: Ordering[B]): Option[A] = + knownSize match { + case -1 => reduceLeftOptionIterator[A](ord.min) + case 0 => None + case _ => Some(reduceLeft(ord.min)) + } /** Finds the largest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the largest element of this $coll with respect to the ordering `ord`. - */ - def max[B >: A](implicit ord: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.max") - reduceLeft(ord.max) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the largest element of this $coll with respect to the ordering `ord`. + */ + def max[B >: A](implicit ord: Ordering[B]): A = + knownSize match { + case -1 => reduceLeftIterator[A](throw new UnsupportedOperationException("empty.max"))(ord.max) + case 0 => throw new UnsupportedOperationException("empty.max") + case _ => reduceLeft(ord.max) + } /** Finds the largest element. - * - * $willNotTerminateInf - * - * @param ord An ordering to be used for comparing elements. - * @tparam B The type over which the ordering is defined. - * @return an option value containing the largest element of this $coll with - * respect to the ordering `ord`. - */ - def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(max(ord)) - } + * + * $willNotTerminateInf + * + * @param ord An ordering to be used for comparing elements. + * @tparam B The type over which the ordering is defined. + * @return an option value containing the largest element of this $coll with + * respect to the ordering `ord`. + */ + def maxOption[B >: A](implicit ord: Ordering[B]): Option[A] = + knownSize match { + case -1 => reduceLeftOptionIterator[A](ord.max) + case 0 => None + case _ => Some(reduceLeft(ord.max)) + } /** Finds the first element which yields the largest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the first element of this $coll with the largest value measured by function f - * with respect to the ordering `cmp`. - */ - def maxBy[B](f: A => B)(implicit cmp: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.maxBy") - - class Maximizer extends runtime.AbstractFunction1[A, Unit] { - var maxF: B = null.asInstanceOf[B] - var maxElem: A = null.asInstanceOf[A] - var first = true - def apply(elem: A) = { - val fx = f(elem) - if (first && { first = false ; true } || cmp.gt(fx, maxF)) { - maxElem = elem - maxF = fx + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the first element of this $coll with the largest value measured by function f + * with respect to the ordering `cmp`. + */ + def maxBy[B](f: A => B)(implicit ord: Ordering[B]): A = + knownSize match { + case 0 => throw new UnsupportedOperationException("empty.maxBy") + case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).result + } + + private class Maximized[X, B](descriptor: String)(f: X => B)(implicit cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { + var maxElem: X = null.asInstanceOf[X] + var maxF: B = null.asInstanceOf[B] + var nonEmpty = false + def toOption: Option[X] = if (nonEmpty) Some(maxElem) else None + def result: X = if (nonEmpty) maxElem else throw new UnsupportedOperationException(s"empty.$descriptor") + def apply(m: Maximized[X, B], a: X): Maximized[X, B] = + if (m.nonEmpty) { + val fa = f(a) + if (cmp(fa, maxF)) { + maxF = fa + maxElem = a } + m + } + else { + m.nonEmpty = true + m.maxElem = a + m.maxF = f(a) + m } - } - val maximizer = new Maximizer - foreach(maximizer) - maximizer.maxElem } /** Finds the first element which yields the largest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @return an option value containing the first element of this $coll with the - * largest value measured by function f with respect to the ordering `cmp`. - */ - def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(maxBy(f)(cmp)) - } + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @return an option value containing the first element of this $coll with the + * largest value measured by function f with respect to the ordering `cmp`. + */ + def maxByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + knownSize match { + case 0 => None + case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).toOption + } /** Finds the first element which yields the smallest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @throws UnsupportedOperationException if this $coll is empty. - * @return the first element of this $coll with the smallest value measured by function f - * with respect to the ordering `cmp`. - */ - def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = { - if (isEmpty) - throw new UnsupportedOperationException("empty.minBy") - - class Minimizer extends runtime.AbstractFunction1[A, Unit] { - var minF: B = null.asInstanceOf[B] - var minElem: A = null.asInstanceOf[A] - var first = true - def apply(elem: A) = { - val fx = f(elem) - if (first && { first = false ; true } || cmp.lt(fx, minF)) { - minElem = elem - minF = fx - } - } + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @throws UnsupportedOperationException if this $coll is empty. + * @return the first element of this $coll with the smallest value measured by function f + * with respect to the ordering `cmp`. + */ + def minBy[B](f: A => B)(implicit ord: Ordering[B]): A = + knownSize match { + case 0 => throw new UnsupportedOperationException("empty.minBy") + case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).result } - val minimizer = new Minimizer - foreach(minimizer) - minimizer.minElem - } /** Finds the first element which yields the smallest value measured by function f. - * - * $willNotTerminateInf - * - * @param cmp An ordering to be used for comparing elements. - * @tparam B The result type of the function f. - * @param f The measuring function. - * @return an option value containing the first element of this $coll - * with the smallest value measured by function f - * with respect to the ordering `cmp`. - */ - def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = { - if (isEmpty) - None - else - Some(minBy(f)(cmp)) - } + * + * $willNotTerminateInf + * + * @param cmp An ordering to be used for comparing elements. + * @tparam B The result type of the function f. + * @param f The measuring function. + * @return an option value containing the first element of this $coll + * with the smallest value measured by function f + * with respect to the ordering `cmp`. + */ + def minByOption[B](f: A => B)(implicit ord: Ordering[B]): Option[A] = + knownSize match { + case 0 => None + case _ => foldLeft(new Maximized[A, B]("minBy")(f)(ord.lt))((m, a) => m(m, a)).toOption + } /** Finds the first element of the $coll for which the given partial - * function is defined, and applies the partial function to it. - * - * $mayNotTerminateInf - * $orderDependent - * - * @param pf the partial function - * @return an option value containing pf applied to the first - * value for which it is defined, or `None` if none exists. - * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` - */ + * function is defined, and applies the partial function to it. + * + * $mayNotTerminateInf + * $orderDependent + * + * @param pf the partial function + * @return an option value containing pf applied to the first + * value for which it is defined, or `None` if none exists. + * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` + */ def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = { // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself // (Tested to be lower-overhead than runWith. Would be better yet to not need to (formally) allocate it) @@ -1145,74 +1167,73 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => a.hasNext == b.hasNext } - /** Displays all elements of this $coll in a string using start, end, and - * separator strings. - * - * Delegates to addString, which can be overridden. - * - * @param start the starting string. - * @param sep the separator string. - * @param end the ending string. - * @return a string representation of this $coll. The resulting string - * begins with the string `start` and ends with the string - * `end`. Inside, the string representations (w.r.t. the method - * `toString`) of all elements of this $coll are separated by - * the string `sep`. - * - * @example `List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"` - */ + /** Displays all elements of this $coll in a string using start, end, and separator strings. + * + * Delegates to addString, which can be overridden. + * + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return a string representation of this $coll. The resulting string + * begins with the string `start` and ends with the string + * `end`. Inside, the string representations (w.r.t. the method + * `toString`) of all elements of this $coll are separated by + * the string `sep`. + * + * @example `List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"` + */ final def mkString(start: String, sep: String, end: String): String = - if (isEmpty) start + end + if (knownSize == 0) start + end else addString(new StringBuilder(), start, sep, end).result() /** Displays all elements of this $coll in a string using a separator string. - * - * Delegates to addString, which can be overridden. - * - * @param sep the separator string. - * @return a string representation of this $coll. In the resulting string - * the string representations (w.r.t. the method `toString`) - * of all elements of this $coll are separated by the string `sep`. - * - * @example `List(1, 2, 3).mkString("|") = "1|2|3"` - */ + * + * Delegates to addString, which can be overridden. + * + * @param sep the separator string. + * @return a string representation of this $coll. In the resulting string + * the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * @example `List(1, 2, 3).mkString("|") = "1|2|3"` + */ @inline final def mkString(sep: String): String = mkString("", sep, "") /** Displays all elements of this $coll in a string. - * - * Delegates to addString, which can be overridden. - * - * @return a string representation of this $coll. In the resulting string - * the string representations (w.r.t. the method `toString`) - * of all elements of this $coll follow each other without any - * separator string. - */ + * + * Delegates to addString, which can be overridden. + * + * @return a string representation of this $coll. In the resulting string + * the string representations (w.r.t. the method `toString`) + * of all elements of this $coll follow each other without any + * separator string. + */ @inline final def mkString: String = mkString("") /** Appends all elements of this $coll to a string builder using start, end, and separator strings. - * The written text begins with the string `start` and ends with the string `end`. - * Inside, the string representations (w.r.t. the method `toString`) - * of all elements of this $coll are separated by the string `sep`. - * - * Example: - * - * {{{ - * scala> val a = List(1,2,3,4) - * a: List[Int] = List(1, 2, 3, 4) - * - * scala> val b = new StringBuilder() - * b: StringBuilder = - * - * scala> a.addString(b , "List(" , ", " , ")") - * res5: StringBuilder = List(1, 2, 3, 4) - * }}} - * - * @param b the string builder to which elements are appended. - * @param start the starting string. - * @param sep the separator string. - * @param end the ending string. - * @return the string builder `b` to which elements were appended. - */ + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> a.addString(b , "List(" , ", " , ")") + * res5: StringBuilder = List(1, 2, 3, 4) + * }}} + * + * @param b the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ def addString(b: StringBuilder, start: String, sep: String, end: String): b.type = { val jsb = b.underlying if (start.length != 0) jsb.append(start) @@ -1229,57 +1250,59 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => } /** Appends all elements of this $coll to a string builder using a separator string. - * The written text consists of the string representations (w.r.t. the method `toString`) - * of all elements of this $coll, separated by the string `sep`. - * - * Example: - * - * {{{ - * scala> val a = List(1,2,3,4) - * a: List[Int] = List(1, 2, 3, 4) - * - * scala> val b = new StringBuilder() - * b: StringBuilder = - * - * scala> a.addString(b, ", ") - * res0: StringBuilder = 1, 2, 3, 4 - * }}} - * - * @param b the string builder to which elements are appended. - * @param sep the separator string. - * @return the string builder `b` to which elements were appended. - */ + * The written text consists of the string representations (w.r.t. the method `toString`) + * of all elements of this $coll, separated by the string `sep`. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> a.addString(b, ", ") + * res0: StringBuilder = 1, 2, 3, 4 + * }}} + * + * @param b the string builder to which elements are appended. + * @param sep the separator string. + * @return the string builder `b` to which elements were appended. + */ @inline final def addString(b: StringBuilder, sep: String): b.type = addString(b, "", sep, "") /** Appends all elements of this $coll to a string builder. - * The written text consists of the string representations (w.r.t. the method - * `toString`) of all elements of this $coll without any separator string. - * - * Example: - * - * {{{ - * scala> val a = List(1,2,3,4) - * a: List[Int] = List(1, 2, 3, 4) - * - * scala> val b = new StringBuilder() - * b: StringBuilder = - * - * scala> val h = a.addString(b) - * h: StringBuilder = 1234 - * }}} - - * @param b the string builder to which elements are appended. - * @return the string builder `b` to which elements were appended. - */ + * The written text consists of the string representations (w.r.t. the method + * `toString`) of all elements of this $coll without any separator string. + * + * Example: + * + * {{{ + * scala> val a = List(1,2,3,4) + * a: List[Int] = List(1, 2, 3, 4) + * + * scala> val b = new StringBuilder() + * b: StringBuilder = + * + * scala> val h = a.addString(b) + * h: StringBuilder = 1234 + * }}} + * + * @param b the string builder to which elements are appended. + * @return the string builder `b` to which elements were appended. + */ @inline final def addString(b: StringBuilder): b.type = addString(b, "") /** Given a collection factory `factory`, convert this collection to the appropriate - * representation for the current element type `A`. Example uses: - * - * xs.to(List) - * xs.to(ArrayBuffer) - * xs.to(BitSet) // for xs: Iterable[Int] - */ + * representation for the current element type `A`. Example uses: + * + * {{{ + * xs.to(List) + * xs.to(ArrayBuffer) + * xs.to(BitSet) // for xs: Iterable[Int] + * }}} + */ def to[C1](factory: Factory[A, C1]): C1 = factory.fromSpecific(this) @deprecated("Use .iterator instead of .toIterator", "2.13.0") @@ -1294,9 +1317,8 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def toSet[B >: A]: immutable.Set[B] = immutable.Set.from(this) - /** - * @return This collection as a `Seq[A]`. This is equivalent to `to(Seq)` but might be faster. - */ + /** @return This collection as a `Seq[A]`. This is equivalent to `to(Seq)` but might be faster. + */ def toSeq: immutable.Seq[A] = immutable.Seq.from(this) def toIndexedSeq: immutable.IndexedSeq[A] = immutable.IndexedSeq.from(this) diff --git a/test/junit/scala/collection/IterableTest.scala b/test/junit/scala/collection/IterableTest.scala index 3a3495d2602b..a03874ec8b50 100644 --- a/test/junit/scala/collection/IterableTest.scala +++ b/test/junit/scala/collection/IterableTest.scala @@ -1,7 +1,7 @@ package scala.collection import org.junit.{Assert, Test} -import Assert.{assertEquals, assertTrue} +import Assert.{assertEquals, assertFalse, assertTrue} import scala.annotation.nowarn import scala.collection.immutable.{ArraySeq, List, Range, Vector} @@ -275,8 +275,8 @@ class IterableTest { case i: Int => Left(i) case s: String => Right(s) } - assertEquals(left, Seq(1, 2, 3, 4 ,5)) - assertEquals(right, Seq("1", "2", "3", "4" ,"5")) + assertEquals(Seq(1, 2, 3, 4 ,5), left) + assertEquals(Seq("1", "2", "3", "4" ,"5"), right) } @deprecated("Uses deprecated hasDefiniteSize, extends HashMap", since="2.13.0") @@ -284,81 +284,190 @@ class IterableTest { def hasDefiniteSize(): Unit = { import scala.{collection => c} import scala.collection.{mutable => m, immutable => i} - assertEquals(true, Some(1).hasDefiniteSize) - assertEquals(true, None.hasDefiniteSize) - assertEquals(true, Option(1).hasDefiniteSize) - assertEquals(true, Array(1).hasDefiniteSize) - assertEquals(true, "a".hasDefiniteSize) - assertEquals(true, c.BitSet(1).hasDefiniteSize) - assertEquals(false, scala.io.Source.fromString("a").buffered.hasDefiniteSize) - assertEquals(true, c.IndexedSeq(1).hasDefiniteSize) - assertEquals(true, c.IndexedSeq(1).view.hasDefiniteSize) - assertEquals(true, c.Iterable(1).hasDefiniteSize) - assertEquals(false, c.Iterator(1).hasDefiniteSize) - assertEquals(true, c.LinearSeq(1).hasDefiniteSize) - assertEquals(true, c.Map(1 -> 1).hasDefiniteSize) - assertEquals(true, c.Map(1 -> 1).view.hasDefiniteSize) - assertEquals(true, c.Seq(1).hasDefiniteSize) - assertEquals(true, c.Seq(1).view.hasDefiniteSize) - assertEquals(true, c.Set(1).hasDefiniteSize) - assertEquals(true, c.SortedMap(1 -> 1).hasDefiniteSize) - assertEquals(true, c.SortedSet(1).hasDefiniteSize) - assertEquals(true, i.BitSet(1).hasDefiniteSize) - assertEquals(true, i.HashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.HashSet(1).hasDefiniteSize) - assertEquals(true, i.IndexedSeq(1).hasDefiniteSize) - assertEquals(true, i.IntMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.Iterable(1).hasDefiniteSize) - assertEquals(true, i.LinearSeq(1).hasDefiniteSize) - assertEquals(true, i.List(1).hasDefiniteSize) - assertEquals(true, i.ListMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.ListSet(1).hasDefiniteSize) - assertEquals(true, i.LongMap(1L -> 1).hasDefiniteSize) - assertEquals(true, i.Map(1 -> 1).hasDefiniteSize) - assertEquals(true, i.Nil.hasDefiniteSize) - assertEquals(true, (1L to 1L).hasDefiniteSize) - assertEquals(true, i.Queue(1).hasDefiniteSize) - assertEquals(true, (1 to 1).hasDefiniteSize) - assertEquals(true, i.Seq(1).hasDefiniteSize) - assertEquals(true, i.Set(1).hasDefiniteSize) - assertEquals(true, i.SortedMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.SortedSet(1).hasDefiniteSize) - assertEquals(false, i.Stream(1).hasDefiniteSize) - assertEquals(true, i.TreeMap(1 -> 1).hasDefiniteSize) - assertEquals(true, i.TreeSet(1).hasDefiniteSize) - assertEquals(true, i.Vector(1).hasDefiniteSize) - assertEquals(false, i.Vector(1).iterator.hasDefiniteSize) - assertEquals(true, m.AnyRefMap(Nil -> 1).hasDefiniteSize) - assertEquals(true, m.ArrayBuffer(1).hasDefiniteSize) - assertEquals(true, m.ArrayBuffer(1).view.hasDefiniteSize) - assertEquals(true, m.BitSet(1).hasDefiniteSize) - assertEquals(true, m.Buffer(1).hasDefiniteSize) - assertEquals(true, m.HashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.HashSet(1).hasDefiniteSize) - assertEquals(true, m.IndexedSeq(1).hasDefiniteSize) - assertEquals(true, m.Iterable(1).hasDefiniteSize) - assertEquals(true, m.LinkedHashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.LinkedHashSet(1).hasDefiniteSize) - assertEquals(true, m.ListBuffer(1).hasDefiniteSize) - assertEquals(true, m.ListMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.LongMap(1L -> 1).hasDefiniteSize) - assertEquals(true, m.Map(1 -> 1).hasDefiniteSize) + assertTrue(Some(1).hasDefiniteSize) + assertTrue(None.hasDefiniteSize) + assertTrue(Option(1).hasDefiniteSize) + assertTrue(Array(1).hasDefiniteSize) + assertTrue("a".hasDefiniteSize) + assertTrue(c.BitSet(1).hasDefiniteSize) + assertFalse(scala.io.Source.fromString("a").buffered.hasDefiniteSize) + assertTrue(c.IndexedSeq(1).hasDefiniteSize) + assertTrue(c.IndexedSeq(1).view.hasDefiniteSize) + assertTrue(c.Iterable(1).hasDefiniteSize) + assertFalse(c.Iterator(1).hasDefiniteSize) + assertTrue(c.LinearSeq(1).hasDefiniteSize) + assertTrue(c.Map(1 -> 1).hasDefiniteSize) + assertTrue(c.Map(1 -> 1).view.hasDefiniteSize) + assertTrue(c.Seq(1).hasDefiniteSize) + assertTrue(c.Seq(1).view.hasDefiniteSize) + assertTrue(c.Set(1).hasDefiniteSize) + assertTrue(c.SortedMap(1 -> 1).hasDefiniteSize) + assertTrue(c.SortedSet(1).hasDefiniteSize) + assertTrue(i.BitSet(1).hasDefiniteSize) + assertTrue(i.HashMap(1 -> 1).hasDefiniteSize) + assertTrue(i.HashSet(1).hasDefiniteSize) + assertTrue(i.IndexedSeq(1).hasDefiniteSize) + assertTrue(i.IntMap(1 -> 1).hasDefiniteSize) + assertTrue(i.Iterable(1).hasDefiniteSize) + assertTrue(i.LinearSeq(1).hasDefiniteSize) + assertTrue(i.List(1).hasDefiniteSize) + assertTrue(i.ListMap(1 -> 1).hasDefiniteSize) + assertTrue(i.ListSet(1).hasDefiniteSize) + assertTrue(i.LongMap(1L -> 1).hasDefiniteSize) + assertTrue(i.Map(1 -> 1).hasDefiniteSize) + assertTrue(i.Nil.hasDefiniteSize) + assertTrue((1L to 1L).hasDefiniteSize) + assertTrue(i.Queue(1).hasDefiniteSize) + assertTrue((1 to 1).hasDefiniteSize) + assertTrue(i.Seq(1).hasDefiniteSize) + assertTrue(i.Set(1).hasDefiniteSize) + assertTrue(i.SortedMap(1 -> 1).hasDefiniteSize) + assertTrue(i.SortedSet(1).hasDefiniteSize) + assertFalse(i.Stream(1).hasDefiniteSize) + assertTrue(i.TreeMap(1 -> 1).hasDefiniteSize) + assertTrue(i.TreeSet(1).hasDefiniteSize) + assertTrue(i.Vector(1).hasDefiniteSize) + assertFalse(i.Vector(1).iterator.hasDefiniteSize) + assertTrue(m.AnyRefMap(Nil -> 1).hasDefiniteSize) + assertTrue(m.ArrayBuffer(1).hasDefiniteSize) + assertTrue(m.ArrayBuffer(1).view.hasDefiniteSize) + assertTrue(m.BitSet(1).hasDefiniteSize) + assertTrue(m.Buffer(1).hasDefiniteSize) + assertTrue(m.HashMap(1 -> 1).hasDefiniteSize) + assertTrue(m.HashSet(1).hasDefiniteSize) + assertTrue(m.IndexedSeq(1).hasDefiniteSize) + assertTrue(m.Iterable(1).hasDefiniteSize) + assertTrue(m.LinkedHashMap(1 -> 1).hasDefiniteSize) + assertTrue(m.LinkedHashSet(1).hasDefiniteSize) + assertTrue(m.ListBuffer(1).hasDefiniteSize) + assertTrue(m.ListMap(1 -> 1).hasDefiniteSize) + assertTrue(m.LongMap(1L -> 1).hasDefiniteSize) + assertTrue(m.Map(1 -> 1).hasDefiniteSize) assertTrue((new m.HashMap[Int, m.Set[Int]] with m.MultiMap[Int, Int]).hasDefiniteSize) // deprecated extension - assertEquals(true, m.OpenHashMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.PriorityQueue(1).hasDefiniteSize) - assertEquals(true, m.Queue(1).hasDefiniteSize) - assertEquals(true, m.Seq(1).hasDefiniteSize) - assertEquals(true, m.Set(1).hasDefiniteSize) - assertEquals(true, m.SortedMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.SortedSet(1).hasDefiniteSize) - assertEquals(true, m.Stack(1).hasDefiniteSize) - assertEquals(true, (new m.StringBuilder()).hasDefiniteSize) - assertEquals(true, m.TreeMap(1 -> 1).hasDefiniteSize) - assertEquals(true, m.TreeSet(1).hasDefiniteSize) - assertEquals(true, m.UnrolledBuffer(1).hasDefiniteSize) - assertEquals(true, m.WeakHashMap(1 -> 1).hasDefiniteSize) - assertEquals(false, scala.io.Source.fromString("hello").hasDefiniteSize) - assertEquals(true, (List(1), List(2)).zipped.hasDefiniteSize) - assertEquals(true, (List(1), List(2), List(3)).zipped.hasDefiniteSize) + assertTrue(m.OpenHashMap(1 -> 1).hasDefiniteSize) + assertTrue(m.PriorityQueue(1).hasDefiniteSize) + assertTrue(m.Queue(1).hasDefiniteSize) + assertTrue(m.Seq(1).hasDefiniteSize) + assertTrue(m.Set(1).hasDefiniteSize) + assertTrue(m.SortedMap(1 -> 1).hasDefiniteSize) + assertTrue(m.SortedSet(1).hasDefiniteSize) + assertTrue(m.Stack(1).hasDefiniteSize) + assertTrue((new m.StringBuilder()).hasDefiniteSize) + assertTrue(m.TreeMap(1 -> 1).hasDefiniteSize) + assertTrue(m.TreeSet(1).hasDefiniteSize) + assertTrue(m.UnrolledBuffer(1).hasDefiniteSize) + assertTrue(m.WeakHashMap(1 -> 1).hasDefiniteSize) + assertFalse(scala.io.Source.fromString("hello").hasDefiniteSize) + assertTrue((List(1), List(2)).zipped.hasDefiniteSize) + assertTrue((List(1), List(2), List(3)).zipped.hasDefiniteSize) } + + class SingleUseIterable[A] private (xs: A*) extends IterableOnce[A] with IterableOnceOps[A, SingleUseIterable, SingleUseIterable[A]] with UseIterableOps[A, SingleUseIterable, SingleUseIterable[A]] { + private var iterated = false + override def iterator = { + assertFalse("Attempted to re-iterate!", iterated) + iterated = true + Iterator(xs: _*) + } + } + object SingleUseIterable { + def apply[A](xs: A*): SingleUseIterable[A] = new SingleUseIterable(xs: _*) + } + trait UseIterableOps[A, CC[_], C] { + def collect[B](pf: PartialFunction[A,B]): CC[B] = ??? + def drop(n: Int): C = ??? + def dropWhile(p: A => Boolean): C = ??? + def filter(p: A => Boolean): C = ??? + def filterNot(pred: A => Boolean): C = ??? + def flatMap[B](f: A => scala.collection.IterableOnce[B]): CC[B] = ??? + def flatten[B](implicit asIterable: A => scala.collection.IterableOnce[B]): CC[B] = ??? + def map[B](f: A => B): CC[B] = ??? + def scanLeft[B](z: B)(op: (B, A) => B): CC[B] = ??? + def slice(from: Int, until: Int): C = ??? + def span(p: A => Boolean): (C, C) = ??? + def take(n: Int): C = ??? + def takeWhile(p: A => Boolean): C = ??? + def tapEach[U](f: A => U): C = ??? + def zipWithIndex: CC[(A, Int)] = ??? + } + class ZeroUseIterable[A] private () extends IterableOnce[A] with IterableOnceOps[A, ZeroUseIterable, ZeroUseIterable[A]] with UseIterableOps[A, ZeroUseIterable, ZeroUseIterable[A]] { + override def iterator = fail("Attempted to iterate!") + override def knownSize = 0 + } + object ZeroUseIterable { + def apply[A](): ZeroUseIterable[A] = new ZeroUseIterable() + } + + // testing unknown size where iterator isEmpty/nonEmpty and iterator is traversed only once; + // testing knownSize == 0 and iterator is not queried. + + @Test def `IterableOnceOps.sum consumes one iterator`: Unit = assertEquals(10, SingleUseIterable(1, 2, 3, 4).sum) + @Test def `IterableOnceOps.sum of empty iterator`: Unit = assertEquals(0, SingleUseIterable[Int]().sum) + @Test def `IterableOnceOps.sum consumes no iterator`: Unit = assertEquals(0, ZeroUseIterable[Int]().sum) + @Test def `IterableOnceOps.sum of one iterator`: Unit = assertEquals(42, SingleUseIterable[Int](42).sum) + + @Test def `IterableOnceOps.product consumes one iterator`: Unit = assertEquals(24, SingleUseIterable(1, 2, 3, 4).product) + @Test def `IterableOnceOps.product of empty iterator`: Unit = assertEquals(1, SingleUseIterable[Int]().product) + @Test def `IterableOnceOps.product consumes no iterator`: Unit = assertEquals(1, ZeroUseIterable[Int]().product) + @Test def `IterableOnceOps.product of one iterator`: Unit = assertEquals(42, SingleUseIterable[Int](42).product) + + @Test def `IterableOnceOps.min consumes one iterator`: Unit = assertEquals(27, SingleUseIterable(42, 27, 37).min) + @Test def `IterableOnceOps.min of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().min, _.contains("min")) + @Test def `IterableOnceOps.min consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().min, _.contains("min")) + + @Test def `IterableOnceOps.minBy consumes one iterator`: Unit = assertEquals(27, SingleUseIterable(42, 27, 37).minBy(_ * 2)) + @Test def `IterableOnceOps.minBy of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().minBy(_ * 2), _.contains("minBy")) + @Test def `IterableOnceOps.minBy consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().minBy(_ * 2), _.contains("minBy")) + + @Test def `IterableOnceOps.minOption consumes one iterator`: Unit = assertEquals(Some(27), SingleUseIterable(42, 27, 37).minOption) + @Test def `IterableOnceOps.minOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().minOption) + @Test def `IterableOnceOps.minOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().minOption) + + @Test def `IterableOnceOps.minByOption consumes one iterator`: Unit = assertEquals(Some(27), SingleUseIterable(42, 27, 37).minByOption(_ * 2)) + @Test def `IterableOnceOps.minByOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().minByOption(_ * 2)) + @Test def `IterableOnceOps.minByOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().minByOption(_ * 2)) + + @Test def `IterableOnceOps.max consumes one iterator`: Unit = assertEquals(42, SingleUseIterable(42, 27, 37).max) + @Test def `IterableOnceOps.max of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().max, _.contains("max")) + @Test def `IterableOnceOps.max consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().max, _.contains("max")) + + @Test def `IterableOnceOps.maxBy consumes one iterator`: Unit = assertEquals(42, SingleUseIterable(42, 27, 37).maxBy(_ * 2)) + @Test def `IterableOnceOps.maxBy of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().maxBy(_ * 2), _.contains("maxBy")) + @Test def `IterableOnceOps.maxBy consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().maxBy(_ * 2), _.contains("maxBy")) + + @Test def `IterableOnceOps.maxOption consumes one iterator`: Unit = assertEquals(Some(42), SingleUseIterable(42, 27, 37).maxOption) + @Test def `IterableOnceOps.maxOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().maxOption) + @Test def `IterableOnceOps.maxOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().maxOption) + + @Test def `IterableOnceOps.maxByOption consumes one iterator`: Unit = assertEquals(Some(42), SingleUseIterable(42, 27, 37).maxByOption(_ * 2)) + @Test def `IterableOnceOps.maxByOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().maxByOption(_ * 2)) + @Test def `IterableOnceOps.maxByOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().maxByOption(_ * 2)) + + @Test def `IterableOnceOps.reduceLeft consumes one iterator`: Unit = assertEquals(106, SingleUseIterable(42, 27, 37).reduceLeft(_ + _)) + @Test def `IterableOnceOps.reduceLeft of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().reduceLeft(_ + _), _.contains("reduceLeft")) + @Test def `IterableOnceOps.reduceLeft consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().reduceLeft(_ + _), _.contains("reduceLeft")) + + @Test def `IterableOnceOps.reduceLeftOption consumes one iterator`: Unit = assertEquals(Some(10), SingleUseIterable(1, 2, 3, 4).reduceLeftOption(_ + _)) + @Test def `IterableOnceOps.reduceLeftOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().reduceLeftOption(_ + _)) + @Test def `IterableOnceOps.reduceLeftOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().reduceLeftOption(_ + _)) + + @Test def `IterableOnceOps.reduceRight consumes one iterator`: Unit = assertEquals(106, SingleUseIterable(42, 27, 37).reduceRight(_ + _)) + @Test def `IterableOnceOps.reduceRight of empty iterator`: Unit = + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().reduceRight(_ + _), _.contains("reduceRight")) + @Test def `IterableOnceOps.reduceRight consumes no iterator`: Unit = + assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().reduceRight(_ + _), _.contains("reduceRight")) + + @Test def `IterableOnceOps.reduceRightOption consumes one iterator`: Unit = assertEquals(Some(10), SingleUseIterable(1, 2, 3, 4).reduceRightOption(_ + _)) + @Test def `IterableOnceOps.reduceRightOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().reduceRightOption(_ + _)) + @Test def `IterableOnceOps.reduceRightOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().reduceRightOption(_ + _)) } From 4fb18e47841878367fd5d3ae6ad1197c00ef23b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 24 Nov 2022 21:33:33 +0100 Subject: [PATCH 080/261] Do not catch {Array,String}IndexOutOfBoundsException. That does not work in Scala.js, where these exceptions are undefined behavior. See https://www.scala-js.org/doc/semantics.html#undefined-behaviors Instead, perform bounds checks ahead of running into the IOOB. --- src/library/scala/collection/ArrayOps.scala | 16 +++++++++------- src/library/scala/collection/StringOps.scala | 10 ++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/library/scala/collection/ArrayOps.scala b/src/library/scala/collection/ArrayOps.scala index 35de85f837e0..485427886625 100644 --- a/src/library/scala/collection/ArrayOps.scala +++ b/src/library/scala/collection/ArrayOps.scala @@ -125,11 +125,12 @@ object ArrayOps { private[this] val len = xs.length override def knownSize: Int = len - pos def hasNext: Boolean = pos < len - def next(): A = try { + def next(): A = { + if (pos >= xs.length) Iterator.empty.next() val r = xs(pos) pos += 1 r - } catch { case _: ArrayIndexOutOfBoundsException => Iterator.empty.next() } + } override def drop(n: Int): Iterator[A] = { if (n > 0) { val newPos = pos + n @@ -145,11 +146,12 @@ object ArrayOps { private final class ReverseIterator[@specialized(Specializable.Everything) A](xs: Array[A]) extends AbstractIterator[A] with Serializable { private[this] var pos = xs.length-1 def hasNext: Boolean = pos >= 0 - def next(): A = try { + def next(): A = { + if (pos < 0) Iterator.empty.next() val r = xs(pos) pos -= 1 r - } catch { case _: ArrayIndexOutOfBoundsException => Iterator.empty.next() } + } override def drop(n: Int): Iterator[A] = { if (n > 0) pos = Math.max( -1, pos - n) @@ -227,14 +229,14 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @return the first element of this array. * @throws NoSuchElementException if the array is empty. */ - def head: A = try xs.apply(0) catch { case _: ArrayIndexOutOfBoundsException => throw new NoSuchElementException("head of empty array") } + def head: A = if (nonEmpty) xs.apply(0) else throw new NoSuchElementException("head of empty array") /** Selects the last element. * * @return The last element of this array. * @throws NoSuchElementException If the array is empty. */ - def last: A = try xs.apply(xs.length-1) catch { case _: ArrayIndexOutOfBoundsException => throw new NoSuchElementException("last of empty array") } + def last: A = if (nonEmpty) xs.apply(xs.length-1) else throw new NoSuchElementException("last of empty array") /** Optionally selects the first element. * @@ -500,7 +502,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * @tparam A2 the element type of the second resulting collection * @param f the 'split function' mapping the elements of this array to an [[scala.util.Either]] * - * @return a pair of arrays: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], + * @return a pair of arrays: the first one made of those values returned by `f` that were wrapped in [[scala.util.Left]], * and the second one made of those wrapped in [[scala.util.Right]]. */ def partitionMap[A1: ClassTag, A2: ClassTag](f: A => Either[A1, A2]): (Array[A1], Array[A2]) = { val res1 = ArrayBuilder.make[A1] diff --git a/src/library/scala/collection/StringOps.scala b/src/library/scala/collection/StringOps.scala index ce47c765d534..f0be485af8ae 100644 --- a/src/library/scala/collection/StringOps.scala +++ b/src/library/scala/collection/StringOps.scala @@ -33,21 +33,23 @@ object StringOps { private class StringIterator(private[this] val s: String) extends AbstractIterator[Char] { private[this] var pos = 0 def hasNext: Boolean = pos < s.length - def next(): Char = try { + def next(): Char = { + if (pos >= s.length) Iterator.empty.next() val r = s.charAt(pos) pos += 1 r - } catch { case _: IndexOutOfBoundsException => Iterator.empty.next() } + } } private class ReverseIterator(private[this] val s: String) extends AbstractIterator[Char] { private[this] var pos = s.length-1 def hasNext: Boolean = pos >= 0 - def next(): Char = try { + def next(): Char = { + if (pos < 0) Iterator.empty.next() val r = s.charAt(pos) pos -= 1 r - } catch { case _: IndexOutOfBoundsException => Iterator.empty.next() } + } } private class GroupedIterator(s: String, groupSize: Int) extends AbstractIterator[String] { From a34da0536e88f578cf4e99be1872c2e2b559f1f1 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 28 Nov 2022 08:37:59 +0100 Subject: [PATCH 081/261] remove spurious implicit, PR 10211 follow-up --- src/library/scala/collection/IterableOnce.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/scala/collection/IterableOnce.scala b/src/library/scala/collection/IterableOnce.scala index 52024c49b660..5da3a8aaa06c 100644 --- a/src/library/scala/collection/IterableOnce.scala +++ b/src/library/scala/collection/IterableOnce.scala @@ -1043,7 +1043,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => case _ => foldLeft(new Maximized[A, B]("maxBy")(f)(ord.gt))((m, a) => m(m, a)).result } - private class Maximized[X, B](descriptor: String)(f: X => B)(implicit cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { + private class Maximized[X, B](descriptor: String)(f: X => B)(cmp: (B, B) => Boolean) extends AbstractFunction2[Maximized[X, B], X, Maximized[X, B]] { var maxElem: X = null.asInstanceOf[X] var maxF: B = null.asInstanceOf[B] var nonEmpty = false From 0e3d695935c6913ad65b9016b907a89a27293981 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 20 Oct 2022 11:30:17 +0200 Subject: [PATCH 082/261] fix t7965 so it actually makes sense and runs the old test code was wrong (in one place it said `Int`, whereas `String` is correct) but apparently the wrapper that ran the test was wrong too, so the test wasn't testing anything happily, the wrapping is no longer needed (as we don't need to check the JDK version -- and if someday we do, we now have better support for that in partest, with `javaVersion`) --- test/files/run/t7965.scala | 52 ++++++++++++-------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/test/files/run/t7965.scala b/test/files/run/t7965.scala index df80d4b5bbf2..f6fb25f3cf15 100644 --- a/test/files/run/t7965.scala +++ b/test/files/run/t7965.scala @@ -1,7 +1,5 @@ // Test that scala doesn't apply boxing or varargs conversions to the // @PolymorphicSignature magical methods, MethodHandle#{invoke, invokeExact} -object Test { - val code = """ object O { private def foo = "foo" @@ -13,42 +11,24 @@ object O { import java.lang.invoke._ class Box(var a: Any) -object Test { - def main(args: Array[String]): Unit = { - def lookup(name: String, params: Array[Class[_]], ret: Class[_]) = { - val mt = MethodType.methodType(ret, params) - O.lookup.findVirtual(O.getClass, name, mt) - } - val fooResult = (lookup("foo", Array(), classOf[String]).invokeExact(O): Int) - assert(fooResult == "foo") - - val barResult = (lookup("bar", Array(classOf[Int]), classOf[Int]).invokeExact(O, 42): Int) - assert(barResult == -42) - - val box = new Box(null) - (lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) : Unit) - assert(box.a == "present") - - // Note: Application in statement position in a block in Java also infers return type of Unit, - // but we don't support that, ascribe the type to Unit as above. - // as done in Java. - // lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) - () +object Test extends App { + def lookup(name: String, params: Array[Class[_]], ret: Class[_]) = { + val mt = MethodType.methodType(ret, params) + O.lookup.findVirtual(O.getClass, name, mt) } -} + val fooResult = (lookup("foo", Array(), classOf[String]).invokeExact(O): String) + assert(fooResult == "foo") -""" - def main(args: Array[String]): Unit = { - if (util.Properties.isJavaAtLeast("1.7")) test() - } + val barResult = (lookup("bar", Array(classOf[Int]), classOf[Int]).invokeExact(O, 42): Int) + assert(barResult == -42) - def test() { - import scala.reflect.runtime._ - import scala.tools.reflect.ToolBox + val box = new Box(null) + (lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) : Unit) + assert(box.a == "present") + + // Note: Application in statement position in a block in Java also infers return type of Unit, + // but we don't support that, ascribe the type to Unit as above. + // as done in Java. + // lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) - val m = currentMirror - val tb = m.mkToolBox() - import tb._ - eval(parse(code)) - } } From 534a5584b2b4e13e2c7ce3502868d1775a5d0c69 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 20 Oct 2022 11:56:15 +0200 Subject: [PATCH 083/261] test `VarHandle` support (on JDK 11+-only) --- test/files/run/t12348.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/files/run/t12348.scala diff --git a/test/files/run/t12348.scala b/test/files/run/t12348.scala new file mode 100644 index 000000000000..e7ea2502a67e --- /dev/null +++ b/test/files/run/t12348.scala @@ -0,0 +1,18 @@ +// javaVersion: 11+ +// scalac: -release:11 + +// this a sequel to t7965. that one only tests MethodHandle. JDK 11 added +// signature polymorphic methods to VarHandle, so let's test that too + +// the reason to include `-release:11` is the problem Jason noticed and fixed in +// scala/scala#9930 + +object Test extends App { + import java.lang.invoke._ + import scala.runtime.IntRef + val ref = new scala.runtime.IntRef(0) + val varHandle = MethodHandles.lookup().in(classOf[IntRef]).findVarHandle(classOf[IntRef], "elem", classOf[Int]) + assert(0 == (varHandle.getAndSet(ref, 1): Int)) + assert(1 == (varHandle.getAndSet(ref, 2): Int)) + assert(2 == ref.elem) +} From 2dbf409212a3c1e0fd201d0d79ba215bbc590411 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 29 Nov 2022 17:35:03 -0600 Subject: [PATCH 084/261] backport Scala 3 tests of signature polymorphic methods --- test/files/run/dotty-i11332.scala | 67 ++++++++++++++++++++++++++++++ test/files/run/dotty-i11332b.scala | 20 +++++++++ test/files/run/dotty-t12348.scala | 23 ++++++++++ 3 files changed, 110 insertions(+) create mode 100644 test/files/run/dotty-i11332.scala create mode 100644 test/files/run/dotty-i11332b.scala create mode 100644 test/files/run/dotty-t12348.scala diff --git a/test/files/run/dotty-i11332.scala b/test/files/run/dotty-i11332.scala new file mode 100644 index 000000000000..2473c89db65f --- /dev/null +++ b/test/files/run/dotty-i11332.scala @@ -0,0 +1,67 @@ +import java.lang.invoke._, MethodType.methodType + +class Foo { + def neg(x: Int): Int = -x + def rev(s: String): String = s.reverse + def over(l: Long): String = "long" + def over(i: Int): String = "int" + def unit(s: String): Unit = () + def obj(s: String): Object = s +} + +object Test { + def main(args: Array[String]): Unit = { + val l = MethodHandles.lookup() + val self = new Foo() + val mhNeg = l.findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) + val mhRev = l.findVirtual(classOf[Foo], "rev", methodType(classOf[String], classOf[String])) + val mhOverL = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Long])) + val mhOverI = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Int])) + val mhUnit = l.findVirtual(classOf[Foo], "unit", methodType(classOf[Unit], classOf[String])) + val mhObj = l.findVirtual(classOf[Foo], "obj", methodType(classOf[Any], classOf[String])) + + assert(-42 == (mhNeg.invokeExact(self, 42): Int)) + assert(-33 == (mhNeg.invokeExact(self, 33): Int)) + + assert("oof" == (mhRev.invokeExact(self, "foo"): String)) + assert("rab" == (mhRev.invokeExact(self, "bar"): String)) + + assert("long" == (mhOverL.invokeExact(self, 1L): String)) + assert("int" == (mhOverI.invokeExact(self, 1): String)) + + assert(-3 == (id(mhNeg.invokeExact(self, 3)): Int)) + expectWrongMethod(mhNeg.invokeExact(self, 4)) + + { mhUnit.invokeExact(self, "hi"): Unit; () } // explicit block + val hi2: Unit = mhUnit.invokeExact(self, "hi2") + assert((()) == (hi2: Any)) + def hi3: Unit = mhUnit.invokeExact(self, "hi3") + assert((()) == (hi3: Any)) + + { mhObj.invokeExact(self, "any"); () } // explicit block + val any2 = mhObj.invokeExact(self, "any2") + assert("any2" == any2) + def any3 = mhObj.invokeExact(self, "any3") + assert("any3" == any3) + + expectWrongMethod { + l // explicit chain method call + .findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) + .invokeExact(self, 3) + } + val res4 = { + l // explicit chain method call + .findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) + .invokeExact(self, 4): Int + } + assert(-4 == res4) + } + + def id[T](x: T): T = x + + def expectWrongMethod(op: => Any) = try { + op + throw new AssertionError("expected operation to fail but it didn't") + } catch { case expected: WrongMethodTypeException => () } + +} diff --git a/test/files/run/dotty-i11332b.scala b/test/files/run/dotty-i11332b.scala new file mode 100644 index 000000000000..f98a53fb47b3 --- /dev/null +++ b/test/files/run/dotty-i11332b.scala @@ -0,0 +1,20 @@ +// javaVersion: 11+ +// scalac: -release:11 + +import java.lang.invoke._, MethodType.methodType + +object Test { + def main(args: Array[String]): Unit = { + val l = MethodHandles.lookup() + val mhCL = l.findStatic(classOf[ClassLoader], "getPlatformClassLoader", methodType(classOf[ClassLoader])) + // sanity check that the non-signature-polymorphic invocations work as expected + assert(null != (mhCL.invoke(): ClassLoader)) + assert(null != (mhCL.invoke().asInstanceOf[ClassLoader]: ClassLoader)) + // now go signature polymorphic + assert(null != (mhCL.invokeExact(): ClassLoader)) + // I've commented out this part of the Dotty test because here in Scala 2, + // we didn't implement specifying a signature polymorphic method's return type + // via `asInstanceOf`; we only implemented specifying it via type ascription + // assert(null != (mhCL.invokeExact().asInstanceOf[ClassLoader]: ClassLoader)) + } +} diff --git a/test/files/run/dotty-t12348.scala b/test/files/run/dotty-t12348.scala new file mode 100644 index 000000000000..b655da3012dc --- /dev/null +++ b/test/files/run/dotty-t12348.scala @@ -0,0 +1,23 @@ +// javaVersion: 11+ +// scalac: -release:11 + +import java.lang.invoke._ +import scala.runtime.IntRef + +object Test { + def main(args: Array[String]): Unit = { + val ref = new scala.runtime.IntRef(0) + val varHandle = MethodHandles.lookup() + .in(classOf[IntRef]) + .findVarHandle(classOf[IntRef], "elem", classOf[Int]) + assert(0 == (varHandle.getAndSet(ref, 1): Int)) + assert(1 == (varHandle.getAndSet(ref, 2): Int)) + assert(2 == ref.elem) + + assert((()) == (varHandle.set(ref, 3): Any)) + assert(3 == (varHandle.get(ref): Int)) + + assert(true == (varHandle.compareAndSet(ref, 3, 4): Any)) + assert(4 == (varHandle.get(ref): Int)) + } +} From 9aec1aa89ccba61fa8e666b03a61a1f3a469ac1f Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 30 Nov 2022 16:01:54 -0600 Subject: [PATCH 085/261] fix incorrect comment in test --- test/files/run/dotty-i11332b.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/files/run/dotty-i11332b.scala b/test/files/run/dotty-i11332b.scala index f98a53fb47b3..2627029541fa 100644 --- a/test/files/run/dotty-i11332b.scala +++ b/test/files/run/dotty-i11332b.scala @@ -7,10 +7,9 @@ object Test { def main(args: Array[String]): Unit = { val l = MethodHandles.lookup() val mhCL = l.findStatic(classOf[ClassLoader], "getPlatformClassLoader", methodType(classOf[ClassLoader])) - // sanity check that the non-signature-polymorphic invocations work as expected + // `invoke` and `invokeExact` are both signature polymorphic assert(null != (mhCL.invoke(): ClassLoader)) assert(null != (mhCL.invoke().asInstanceOf[ClassLoader]: ClassLoader)) - // now go signature polymorphic assert(null != (mhCL.invokeExact(): ClassLoader)) // I've commented out this part of the Dotty test because here in Scala 2, // we didn't implement specifying a signature polymorphic method's return type From 6da8d6c43d371871542e8697d47d818bc644b8ec Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 30 Nov 2022 16:36:00 -0600 Subject: [PATCH 086/261] spec: clarify a passage about signature polymorphic methods --- spec/06-expressions.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/06-expressions.md b/spec/06-expressions.md index 391f24762ac5..00437a52f0cf 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -411,9 +411,10 @@ undefined then `$U$` is `scala.AnyRef`. The parameter names `$p_1 , \ldots , p_n ###### Note -On the Java platform version 11 and later, signature polymorphic methods are native, -members of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and have a single -repeated parameter of type `java.lang.Object*`. +On the Java platform version 11 and later, a method is signature polymorphic if it is native, +a member of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and has a single +repeated parameter of type `java.lang.Object*`. (These requirements also work for Java 8, +which had fewer such methods.) ## Method Values From 29b6ed478f9738eb3becde77fe9781e0b4919bb2 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 30 Nov 2022 17:18:45 -0600 Subject: [PATCH 087/261] test handling of Void for signature polymorphic methods --- test/files/run/dotty-i11332.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/files/run/dotty-i11332.scala b/test/files/run/dotty-i11332.scala index 2473c89db65f..9241b922504d 100644 --- a/test/files/run/dotty-i11332.scala +++ b/test/files/run/dotty-i11332.scala @@ -7,6 +7,7 @@ class Foo { def over(i: Int): String = "int" def unit(s: String): Unit = () def obj(s: String): Object = s + def void(v: Void): String = "void" } object Test { @@ -19,6 +20,7 @@ object Test { val mhOverI = l.findVirtual(classOf[Foo], "over", methodType(classOf[String], classOf[Int])) val mhUnit = l.findVirtual(classOf[Foo], "unit", methodType(classOf[Unit], classOf[String])) val mhObj = l.findVirtual(classOf[Foo], "obj", methodType(classOf[Any], classOf[String])) + val mhVoid = l.findVirtual(classOf[Foo], "void", methodType(classOf[String], classOf[Void])) assert(-42 == (mhNeg.invokeExact(self, 42): Int)) assert(-33 == (mhNeg.invokeExact(self, 33): Int)) @@ -44,6 +46,8 @@ object Test { def any3 = mhObj.invokeExact(self, "any3") assert("any3" == any3) + assert("void" == (mhVoid.invokeExact(self, null: Void): String)) + expectWrongMethod { l // explicit chain method call .findVirtual(classOf[Foo], "neg", methodType(classOf[Int], classOf[Int])) From 6665bbae56f27fe265bc7017987e08e7970bb85e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 28 Nov 2022 23:14:20 -0800 Subject: [PATCH 088/261] IterableOnce.isEmpty uses knownSize Also sum and product intercept BigDecimal identity ops that change the math context. This is a workaround restricted to just these methods. --- .../scala/collection/IterableOnce.scala | 39 ++++++++++++++++--- .../junit/scala/collection/IterableTest.scala | 16 ++++++++ test/junit/scala/collection/SeqViewTest.scala | 39 ++++++++++++++++++- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/library/scala/collection/IterableOnce.scala b/src/library/scala/collection/IterableOnce.scala index 5da3a8aaa06c..27cde9e1f8ff 100644 --- a/src/library/scala/collection/IterableOnce.scala +++ b/src/library/scala/collection/IterableOnce.scala @@ -843,7 +843,12 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => * * @return `true` if the $coll contains no elements, `false` otherwise. */ - def isEmpty: Boolean = !iterator.hasNext + def isEmpty: Boolean = + knownSize match { + case -1 => !iterator.hasNext + case 0 => true + case _ => false + } /** Tests whether the $coll is not empty. * @@ -929,7 +934,13 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => i - start } - /** Sums up the elements of this collection. + /** Sums the elements of this collection. + * + * The default implementation uses `reduce` for a known non-empty collection, + * `foldLeft` otherwise. + * + * If `foldLeft` is used, this implementation works around pollution of the math context + * by ignoring the identity element. * * $willNotTerminateInf * @@ -940,12 +951,24 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => */ def sum[B >: A](implicit num: Numeric[B]): B = knownSize match { - case -1 => reduceLeftIterator[B](num.zero)(num.plus) + case -1 => + val z = num.zero + if ((num eq Numeric.BigDecimalIsFractional) || (num eq Numeric.BigDecimalAsIfIntegral)) { + def nonPollutingPlus(x: B, y: B): B = if (x.asInstanceOf[AnyRef] eq z.asInstanceOf[AnyRef]) y else num.plus(x, y) + foldLeft(z)(nonPollutingPlus) + } + else foldLeft(z)(num.plus) case 0 => num.zero case _ => reduce(num.plus) } - /** Multiplies up the elements of this collection. + /** Multiplies together the elements of this collection. + * + * The default implementation uses `reduce` for a known non-empty collection, + * `foldLeft` otherwise. + * + * If `foldLeft` is used, this implementation works around pollution of the math context + * by ignoring the identity element. * * $willNotTerminateInf * @@ -956,7 +979,13 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => */ def product[B >: A](implicit num: Numeric[B]): B = knownSize match { - case -1 => reduceLeftIterator[B](num.one)(num.times) + case -1 => + val u = num.one + if ((num eq Numeric.BigDecimalIsFractional) || (num eq Numeric.BigDecimalAsIfIntegral)) { + def nonPollutingProd(x: B, y: B): B = if (x.asInstanceOf[AnyRef] eq u.asInstanceOf[AnyRef]) y else num.times(x, y) + foldLeft(u)(nonPollutingProd) + } + else foldLeft(u)(num.times) case 0 => num.one case _ => reduce(num.times) } diff --git a/test/junit/scala/collection/IterableTest.scala b/test/junit/scala/collection/IterableTest.scala index a03874ec8b50..bb815d22013f 100644 --- a/test/junit/scala/collection/IterableTest.scala +++ b/test/junit/scala/collection/IterableTest.scala @@ -470,4 +470,20 @@ class IterableTest { @Test def `IterableOnceOps.reduceRightOption consumes one iterator`: Unit = assertEquals(Some(10), SingleUseIterable(1, 2, 3, 4).reduceRightOption(_ + _)) @Test def `IterableOnceOps.reduceRightOption of empty iterator`: Unit = assertEquals(None, SingleUseIterable[Int]().reduceRightOption(_ + _)) @Test def `IterableOnceOps.reduceRightOption consumes no iterator`: Unit = assertEquals(None, ZeroUseIterable[Int]().reduceRightOption(_ + _)) + + @Test def `IterableOnceOps.isEmpty consumes no iterator`: Unit = assertTrue(ZeroUseIterable[Int]().isEmpty) + + @Test def `sum uses my reduce if knownSize > 0`: Unit = { + val reductive = new AbstractIterable[Int] { + val values = List(1, 2, 3, 4, 32) + override def iterator = Iterator.empty + override def reduce[B >: Int](op: (B, B) => B): B = values.reduce(op) // sum, produce + override def reduceLeft[B >: Int](op: (B, Int) => B): B = values.reduceLeft(op) // min, max + override def knownSize = values.size + } + assertEquals(42, reductive.sum) + assertEquals(768, reductive.product) + assertEquals(1, reductive.min) + assertEquals(32, reductive.max) + } } diff --git a/test/junit/scala/collection/SeqViewTest.scala b/test/junit/scala/collection/SeqViewTest.scala index 6b91a34e790d..2bcc46c4b03b 100644 --- a/test/junit/scala/collection/SeqViewTest.scala +++ b/test/junit/scala/collection/SeqViewTest.scala @@ -1,14 +1,51 @@ package scala.collection -import org.junit.Assert._ +import org.junit.Assert.{assertThrows => _, _} import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import scala.tools.testkit.AssertUtil.assertThrows + @RunWith(classOf[JUnit4]) class SeqViewTest { @Test def _toString(): Unit = { assertEquals("SeqView()", Seq(1, 2, 3).view.toString) } + + private def seq(n: Int, maxIterate: Int = -1): Seq[Int] = + new AbstractSeq[Int] { + val values = 1 to n + var remaining = maxIterate + def iterator = { + if (remaining == 0) throw new IllegalStateException() + if (remaining > 0) remaining -= 1 + values.iterator + } + def apply(i: Int) = values(i) + def length = values.length + } + private def seqk(n: Int, maxIterate: Int): Seq[Int] = + new AbstractSeq[Int] { + val values = 1 to n + var remaining = maxIterate + def iterator = { + if (remaining == 0) throw new IllegalStateException() + if (remaining > 0) remaining -= 1 + values.iterator + } + def apply(i: Int) = values(i) + def length = values.length + override def knownSize = length + } + + @Test def `sum of seq view`: Unit = { + assertEquals(10, seq(4).view.sum) + } + @Test def `sum of seq view iterates once`: Unit = { + val sut = seqk(4, 1).view + assertEquals(10, sut.sum) + assertThrows[IllegalStateException](sut.sum) + } } From c9513f1f8338bf3bc9bcbc232c8263484d79055b Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 1 Dec 2022 11:01:10 -0800 Subject: [PATCH 089/261] Push BigDecimal exclusion into Numeric --- project/MimaFilters.scala | 3 + .../scala/collection/IterableOnce.scala | 28 +--- src/library/scala/math/Numeric.scala | 21 ++- .../scala/tools/testkit/AssertUtil.scala | 28 +++- test/junit/scala/math/BigDecimalTest.scala | 133 +++++++++--------- 5 files changed, 114 insertions(+), 99 deletions(-) diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index b2146dd14066..b0c5c8b96823 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -41,6 +41,9 @@ object MimaFilters extends AutoPlugin { // private class used by methods ProblemFilters.exclude[MissingClassProblem]("scala.collection.IterableOnceOps$Maximized"), + + // private object used by methods + ProblemFilters.exclude[MissingClassProblem]("scala.math.Numeric$BigDecimalIsConflicted$"), ) override val buildSettings = Seq( diff --git a/src/library/scala/collection/IterableOnce.scala b/src/library/scala/collection/IterableOnce.scala index 27cde9e1f8ff..a480f8406c46 100644 --- a/src/library/scala/collection/IterableOnce.scala +++ b/src/library/scala/collection/IterableOnce.scala @@ -936,11 +936,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => /** Sums the elements of this collection. * - * The default implementation uses `reduce` for a known non-empty collection, - * `foldLeft` otherwise. - * - * If `foldLeft` is used, this implementation works around pollution of the math context - * by ignoring the identity element. + * The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise. * * $willNotTerminateInf * @@ -951,24 +947,14 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => */ def sum[B >: A](implicit num: Numeric[B]): B = knownSize match { - case -1 => - val z = num.zero - if ((num eq Numeric.BigDecimalIsFractional) || (num eq Numeric.BigDecimalAsIfIntegral)) { - def nonPollutingPlus(x: B, y: B): B = if (x.asInstanceOf[AnyRef] eq z.asInstanceOf[AnyRef]) y else num.plus(x, y) - foldLeft(z)(nonPollutingPlus) - } - else foldLeft(z)(num.plus) + case -1 => foldLeft(num.zero)(num.plus) case 0 => num.zero case _ => reduce(num.plus) } /** Multiplies together the elements of this collection. * - * The default implementation uses `reduce` for a known non-empty collection, - * `foldLeft` otherwise. - * - * If `foldLeft` is used, this implementation works around pollution of the math context - * by ignoring the identity element. + * The default implementation uses `reduce` for a known non-empty collection, `foldLeft` otherwise. * * $willNotTerminateInf * @@ -979,13 +965,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => */ def product[B >: A](implicit num: Numeric[B]): B = knownSize match { - case -1 => - val u = num.one - if ((num eq Numeric.BigDecimalIsFractional) || (num eq Numeric.BigDecimalAsIfIntegral)) { - def nonPollutingProd(x: B, y: B): B = if (x.asInstanceOf[AnyRef] eq u.asInstanceOf[AnyRef]) y else num.times(x, y) - foldLeft(u)(nonPollutingProd) - } - else foldLeft(u)(num.times) + case -1 => foldLeft(num.one)(num.times) case 0 => num.one case _ => reduce(num.times) } diff --git a/src/library/scala/math/Numeric.scala b/src/library/scala/math/Numeric.scala index 935d26df2962..9dca0e4f0199 100644 --- a/src/library/scala/math/Numeric.scala +++ b/src/library/scala/math/Numeric.scala @@ -177,9 +177,20 @@ object Numeric { implicit object DoubleIsFractional extends DoubleIsFractional with Ordering.Double.IeeeOrdering trait BigDecimalIsConflicted extends Numeric[BigDecimal] { - def plus(x: BigDecimal, y: BigDecimal): BigDecimal = x + y - def minus(x: BigDecimal, y: BigDecimal): BigDecimal = x - y - def times(x: BigDecimal, y: BigDecimal): BigDecimal = x * y + // works around pollution of math context by ignoring identity element + def plus(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._0 + if (x eq _0) y else x + y + } + def minus(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._0 + if (x eq _0) -y else x - y + } + // works around pollution of math context by ignoring identity element + def times(x: BigDecimal, y: BigDecimal): BigDecimal = { + import BigDecimalIsConflicted._1 + if (x eq _1) y else x * y + } def negate(x: BigDecimal): BigDecimal = -x def fromInt(x: Int): BigDecimal = BigDecimal(x) def parseString(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption @@ -188,6 +199,10 @@ object Numeric { def toFloat(x: BigDecimal): Float = x.floatValue def toDouble(x: BigDecimal): Double = x.doubleValue } + private object BigDecimalIsConflicted { + private val _0 = BigDecimal(0) // cached zero is ordinarily cached for default math context + private val _1 = BigDecimal(1) // cached one is ordinarily cached for default math context + } trait BigDecimalIsFractional extends BigDecimalIsConflicted with Fractional[BigDecimal] { def div(x: BigDecimal, y: BigDecimal): BigDecimal = x / y diff --git a/src/testkit/scala/tools/testkit/AssertUtil.scala b/src/testkit/scala/tools/testkit/AssertUtil.scala index aee72fda791b..f12bec8d2435 100644 --- a/src/testkit/scala/tools/testkit/AssertUtil.scala +++ b/src/testkit/scala/tools/testkit/AssertUtil.scala @@ -12,22 +12,24 @@ package scala.tools.testkit -import org.junit.Assert.{assertEquals, assertFalse, assertTrue} +import org.junit.Assert.{assertEquals, assertNotEquals} +import org.junit.Assert.{assertFalse, assertTrue} -import scala.reflect.ClassTag -import scala.runtime.ScalaRunTime.stringOf +import scala.annotation.nowarn import scala.collection.mutable import scala.concurrent.{Await, Awaitable} -import scala.util.chaining._ +import scala.reflect.ClassTag +import scala.runtime.BoxesRunTime +import scala.runtime.ScalaRunTime.stringOf import scala.util.{Failure, Success, Try} +import scala.util.chaining._ import scala.util.control.{ControlThrowable, NonFatal} +import java.lang.ref._ +import java.lang.reflect.{Array => _, _} import java.time.Duration import java.util.concurrent.{CountDownLatch, TimeUnit} import java.util.concurrent.atomic.AtomicReference -import java.lang.ref._ -import java.lang.reflect.{Array => _, _} import java.util.IdentityHashMap -import scala.annotation.nowarn /** This module contains additional higher-level assert statements * that are ultimately based on junit.Assert primitives. @@ -62,6 +64,18 @@ object AssertUtil { def assertEqualStrings(expected: String)(actual: String) = assert(expected == actual, s"Expected:\n${dump(expected)}\nActual:\n${dump(actual)}") + // assertEquals but use BoxesRunTime.equals + // let junit format a message on failure + def assertEqualsAny(expected: Any, actual: Any): Unit = + if (!BoxesRunTime.equals(expected, actual)) assertEquals(expected, actual) + // as a bonus, message is by-name, though retains junit parameter order + def assertEqualsAny(message: => String, expected: Any, actual: Any): Unit = + if (!BoxesRunTime.equals(expected, actual)) assertEquals(message, expected, actual) + def assertNotEqualsAny(expected: Any, actual: Any): Unit = + if (BoxesRunTime.equals(expected, actual)) assertNotEquals(expected, actual) + def assertNotEqualsAny(message: => String, expected: Any, actual: Any): Unit = + if (BoxesRunTime.equals(expected, actual)) assertNotEquals(message, expected, actual) + private final val timeout = 60 * 1000L // wait a minute private implicit class `ref helper`[A](val r: Reference[A]) extends AnyVal { diff --git a/test/junit/scala/math/BigDecimalTest.scala b/test/junit/scala/math/BigDecimalTest.scala index 2c003b6451a0..1655cbf38d13 100644 --- a/test/junit/scala/math/BigDecimalTest.scala +++ b/test/junit/scala/math/BigDecimalTest.scala @@ -1,7 +1,9 @@ package scala.math import org.junit.Test +import org.junit.Assert.{assertEquals, assertNotEquals, assertNull, assertTrue} import java.math.{BigDecimal => BD, MathContext => MC} +import scala.tools.testkit.AssertUtil.{assertEqualsAny, assertNotEqualsAny} /* Tests various maps by making sure they all agree on the same answers. */ class BigDecimalTest { @@ -25,7 +27,7 @@ class BigDecimalTest { BigDecimal("14.19238571927581e6"), BigDecimal("912834718237510238591285")/2 ) - assert(wholes.forall(_.isWhole) && fracs.forall(! _.isWhole)) + assertTrue(wholes.forall(_.isWhole) && fracs.forall(! _.isWhole)) } // Motivated by scala/bug#6699: BigDecimal.isValidDouble behaves unexpectedly @@ -42,10 +44,8 @@ class BigDecimalTest { BigDecimal("10e1000000"), BigDecimal("10e-1000000") ) - assert( - valids.forall(_.isDecimalDouble) && - invalids.forall(! _.isDecimalDouble) - ) + assertTrue(valids.forall(_.isDecimalDouble)) + assertTrue(invalids.forall(! _.isDecimalDouble)) } // Motivated by scala/bug#6173: BigDecimal#isWhole implementation is very heap intensive @@ -54,7 +54,7 @@ class BigDecimalTest { val troublemaker = BigDecimal("1e1000000000") val reasonable = BigDecimal("1e1000") val reasonableInt = reasonable.toBigInt - assert( + assertTrue( reasonable.hashCode == reasonableInt.hashCode && reasonable == reasonableInt && reasonableInt == reasonable && @@ -69,7 +69,7 @@ class BigDecimalTest { def refusesNullTest(): Unit = { def isIAE[A](a: => A) = try { a; false } catch { case iae: IllegalArgumentException => true } def isNPE[A](a: => A) = try { a; false } catch { case npe: NullPointerException => true } - assert( + assertTrue( isIAE(new BigDecimal(null: BD, new MC(2))) && isIAE(new BigDecimal(new BD("5.7"), null: MC)) && isNPE(BigDecimal(null: BigInt)) && @@ -85,7 +85,7 @@ class BigDecimalTest { val bd: BigDecimal = 100000 val l: Long = 100000 val d: Double = 100000 - assert( + assertTrue( d.## == l.## && l.## == bd.## && bd.## == bi.## && @@ -109,7 +109,7 @@ class BigDecimalTest { BigDecimal(1) / BigDecimal(10), BigDecimal(10).pow(-1) ) - for (a <- tenths; b <- tenths) assert(a == b, s"$a != $b but both should be 0.1") + for (a <- tenths; b <- tenths) assertEqualsAny(s"$a != $b but both should be 0.1", a, b) } // Motivated by noticing BigDecimal(123456789, mc6) != BigDecimal(123456789L, mc6) @@ -153,12 +153,12 @@ class BigDecimalTest { ) sameRounding.map(_.zipWithIndex).foreach{ case xs => for ((a,i) <- xs; (b,j) <- xs) { - assert(a == b, s"$a != $b (#$i != #$j) but should be the same") - assert(a.## == b.##, s"Hash code mismatch in equal BigDecimals: #$i != #$j") + assertEqualsAny(s"$a != $b (#$i != #$j) but should be the same", a, b) + assertEquals(s"Hash code mismatch in equal BigDecimals: #$i != #$j", a.##, b.##) } } val List(xs, ys) = sameRounding.map(_.zipWithIndex) - for ((a,i) <- xs; (b,j) <- ys) assert(a != b, s"$a == $b (#$i == #$j) but should be different") + for ((a,i) <- xs; (b,j) <- ys) assertNotEqualsAny(s"$a == $b (#$i == #$j) but should be different", a, b) } // This was unexpectedly truncated in 2.10 @@ -168,7 +168,7 @@ class BigDecimalTest { val same = List[Any]( BigInt(text), BigDecimal(text), BigDecimal(new BD(text)) ) - for (a <- same; b <- same) assert(a == b, s"$a != $b but should be the same") + for (a <- same; b <- same) assertEqualsAny(s"$a != $b but should be the same", a, b) } // Tests attempts to make sane the representation of IEEE binary32 and binary64 @@ -217,43 +217,36 @@ class BigDecimalTest { BigDecimal.decimal((0.1f).toDouble) ) for (a <- different; b <- different if (a ne b)) - assert(a != b, "BigDecimal representations of Double mistakenly conflated") + assertNotEquals("BigDecimal representations of Double mistakenly conflated", a, b) } // Make sure hash code agrees with decimal representation of Double - @Test - def test_SI8970(): Unit = { - assert((0.1).## == BigDecimal(0.1).##) - } + @Test def test_SI8970(): Unit = assertEquals((0.1).##, BigDecimal(0.1).##) // Motivated by the problem of MathContext lost @Test - def testMathContext(): Unit = { - def testPrecision(): Unit = { - val p = 1000 - val n = BigDecimal("1.1", MC.UNLIMITED).pow(p) + def testMathContextPrecision(): Unit = { + val p = 1000 + val n = BigDecimal("1.1", MC.UNLIMITED).pow(p) - // BigDecimal(x: Float, mc: MC), which may not do what you want, is deprecated - assert(BigDecimal(1.1d, MC.UNLIMITED).pow(p) == n) - assert(new BigDecimal(new BD("1.1"), MC.UNLIMITED).pow(p) == n) + // BigDecimal(x: Float, mc: MC), which may not do what you want, is deprecated + assertEquals(n, BigDecimal(1.1d, MC.UNLIMITED).pow(p)) + assertEquals(n, new BigDecimal(new BD("1.1"), MC.UNLIMITED).pow(p)) - assert(BigDecimal.decimal(1.1f, MC.UNLIMITED).pow(p) == n) - assert(BigDecimal.decimal(1.1d, MC.UNLIMITED).pow(p) == n) - assert(BigDecimal.decimal(new BD("1.1"), MC.UNLIMITED).pow(p) == n) - - assert((BigDecimal(11, MC.UNLIMITED) / 10).pow(p) == n) - assert((BigDecimal.decimal(11, MC.UNLIMITED) / 10).pow(p) == n) - } + assertEquals(n, BigDecimal.decimal(1.1f, MC.UNLIMITED).pow(p)) + assertEquals(n, BigDecimal.decimal(1.1d, MC.UNLIMITED).pow(p)) + assertEquals(n, BigDecimal.decimal(new BD("1.1"), MC.UNLIMITED).pow(p)) - def testRounded(): Unit = { - // the default rounding mode is HALF_UP - assert((BigDecimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded == BigDecimal("1.24")) - assert((BigDecimal.decimal(1.23f, new MC(3)) + BigDecimal("0.005")).rounded == BigDecimal("1.24")) - assert((BigDecimal.decimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded == BigDecimal("1.24")) - } + assertEquals(n, (BigDecimal(11, MC.UNLIMITED) / 10).pow(p)) + assertEquals(n, (BigDecimal.decimal(11, MC.UNLIMITED) / 10).pow(p)) + } - testPrecision() - testRounded() + // the default rounding mode is HALF_UP + @Test + def testMathContextRounded(): Unit = { + assertEquals(BigDecimal("1.24"), (BigDecimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded) + assertEquals(BigDecimal("1.24"), (BigDecimal.decimal(1.23f, new MC(3)) + BigDecimal("0.005")).rounded) + assertEquals(BigDecimal("1.24"), (BigDecimal.decimal(1.23d, new MC(3)) + BigDecimal("0.005")).rounded) } // Motivated by scala/bug#10882: Operators for BigDecimal don't use value of mc (MathContext) @@ -262,38 +255,34 @@ class BigDecimalTest { def isAE[A](a: => A): Boolean = try { a; false } catch { case e: ArithmeticException => true } val bd128 = BigDecimal("4.2e1000", MC.DECIMAL128) - assert(bd128 + 10 == bd128) - assert(bd128 - 10 == bd128) - assert(bd128 + BigDecimal("1e100", MC.UNLIMITED) == bd128) - assert(bd128 - BigDecimal("1e100", MC.UNLIMITED) == bd128) - assert(bd128.quot(BigDecimal("1e100", MC.UNLIMITED)) == BigDecimal("4.2e900", MC.DECIMAL128)) - assert(isAE(bd128.quot(BigDecimal("1e100", MC.UNLIMITED) + 1))) - assert(isAE(bd128 % (BigDecimal("1e100", MC.UNLIMITED) + 1))) - assert(isAE(bd128 /% (BigDecimal("1e100", MC.UNLIMITED) + 1))) + assertTrue(bd128 + 10 == bd128) + assertTrue(bd128 - 10 == bd128) + assertTrue(bd128 + BigDecimal("1e100", MC.UNLIMITED) == bd128) + assertTrue(bd128 - BigDecimal("1e100", MC.UNLIMITED) == bd128) + assertTrue(bd128.quot(BigDecimal("1e100", MC.UNLIMITED)) == BigDecimal("4.2e900", MC.DECIMAL128)) + assertTrue(isAE(bd128.quot(BigDecimal("1e100", MC.UNLIMITED) + 1))) + assertTrue(isAE(bd128 % (BigDecimal("1e100", MC.UNLIMITED) + 1))) + assertTrue(isAE(bd128 /% (BigDecimal("1e100", MC.UNLIMITED) + 1))) val bdUnlimited = BigDecimal("4.2e1000", MC.UNLIMITED) - assert(bdUnlimited + 10 > bdUnlimited) - assert(bdUnlimited - 10 < bdUnlimited) - assert(bdUnlimited + BigDecimal("1e100", MC.DECIMAL128) > bdUnlimited) - assert(bdUnlimited - BigDecimal("1e100", MC.DECIMAL128) < bdUnlimited) - assert(bdUnlimited.quot(BigDecimal("1e100", MC.DECIMAL128)) == BigDecimal("4.2e900", MC.UNLIMITED)) + assertTrue(bdUnlimited + 10 > bdUnlimited) + assertTrue(bdUnlimited - 10 < bdUnlimited) + assertTrue(bdUnlimited + BigDecimal("1e100", MC.DECIMAL128) > bdUnlimited) + assertTrue(bdUnlimited - BigDecimal("1e100", MC.DECIMAL128) < bdUnlimited) + assertTrue(bdUnlimited.quot(BigDecimal("1e100", MC.DECIMAL128)) == BigDecimal("4.2e900", MC.UNLIMITED)) } @Test def testIsComparable(): Unit = { - assert(BigDecimal(0.1).isInstanceOf[java.lang.Comparable[_]]) + assertTrue(BigDecimal(0.1).isInstanceOf[java.lang.Comparable[_]]) } - - - @Test - def testBigDecimalSumInList(): Unit = { - + // trick sum into using foldLeft, viz, because the size is unknown + @Test def testBigDecimalSumInList(): Unit = { val bds = List( BigDecimal("1000000000000000000000000.1", MC.UNLIMITED), BigDecimal("9.0000000000000000000000009", MC.UNLIMITED)) - assert(bds.sum == BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED)) - + assertEquals(BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED), bds.sum) } @Test @@ -303,17 +292,31 @@ class BigDecimalTest { BigDecimal("9.00000000000000000000000091", MC.UNLIMITED)) val prod = bds.foldLeft(BigDecimal(1, MC.UNLIMITED))(_ * _) - assert(prod == BigDecimal("9000000000000000000000001.810000000000000000000000091", MC.UNLIMITED)) + assertEquals(BigDecimal("9000000000000000000000001.810000000000000000000000091", MC.UNLIMITED), prod) + assertEquals(prod, bds.product) + } - assert(bds.product == prod) + // trick sum into using foldLeft, viz, because the size is unknown, oh wait this iterator knows its size... + @Test def `sum of iterator of BigDecimal`: Unit = { + val bds = Iterator( + BigDecimal("1000000000000000000000000.1", MC.UNLIMITED), + BigDecimal("9.0000000000000000000000009", MC.UNLIMITED)) + assertEquals(BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED), bds.sum) + } + // trick sum into using reduce, viz, because the size is known + @Test def `sum of vector of BigDecimal`: Unit = { + val bds = Vector( + BigDecimal("1000000000000000000000000.1", MC.UNLIMITED), + BigDecimal("9.0000000000000000000000009", MC.UNLIMITED)) + assertEquals(BigDecimal("1000000000000000000000009.1000000000000000000000009", MC.UNLIMITED), bds.sum) } @Test def testImplicitBigDecimalConversionJavaToScalaHandlesNull(): Unit = { val bdNull: BigDecimal = (null: java.math.BigDecimal): BigDecimal - assert(bdNull == null) + assertNull(bdNull) val bdValue: BigDecimal = (BD.ONE: java.math.BigDecimal): BigDecimal - assert(bdValue.bigDecimal == BD.ONE) + assertEquals(BD.ONE, bdValue.bigDecimal) } } From 077f7143e50e3b61dd9bf629e602a941fdcadacb Mon Sep 17 00:00:00 2001 From: Liang Yan Date: Sun, 13 Nov 2022 21:40:08 +0800 Subject: [PATCH 090/261] new LinkedHashMap/LinkedHashSet implementation The mutable HashMap/HashSet has been rewroten and the performance is better. So rewrote the LinkedHashMap and LinkedHashSet also to improve performance. The detailed data can be seen in the PR. Most codes are same with HashMap/HashSet but some are different: 1. To keep binary compatibility, only api in old solution are updated.The two class LinkedHashMap/LinkedHashSet still don't have parameters. hashcode can't be realized since it needs a new iterator which will break binary compatibility. 2. Add specific method to handle the order when adding/removing the entry. 3. other minor changes. Signed-off-by: Liang Yan --- .../collection/mutable/LinkedHashMap.scala | 382 ++++++++++++++---- .../collection/mutable/LinkedHashSet.scala | 264 ++++++++++-- .../mutable/LinkedHashMapBenchmark2.scala | 216 ++++++++++ 3 files changed, 737 insertions(+), 125 deletions(-) create mode 100644 test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index 22e9b4c53a1d..ddc039ef6bd8 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -14,35 +14,9 @@ package scala package collection package mutable -import scala.annotation.nowarn +import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable -/** $factoryInfo - * @define Coll `LinkedHashMap` - * @define coll linked hash map - */ -@SerialVersionUID(3L) -object LinkedHashMap extends MapFactory[LinkedHashMap] { - - def empty[K, V] = new LinkedHashMap[K, V] - - def from[K, V](it: collection.IterableOnce[(K, V)]) = - it match { - case lhm: LinkedHashMap[K, V] => lhm - case _ => Growable.from(empty[K, V], it) - } - - def newBuilder[K, V] = new GrowableBuilder(empty[K, V]) - - /** Class for the linked hash map entry, used internally. - */ - private[mutable] final class LinkedEntry[K, V](val key: K, var value: V) - extends HashEntry[K, LinkedEntry[K, V]] { - var earlier: LinkedEntry[K, V] = null - var later: LinkedEntry[K, V] = null - } - -} /** This class implements mutable maps using a hashtable. * The iterator and all traversal methods of this class visit elements in the order they were inserted. @@ -70,36 +44,19 @@ class LinkedHashMap[K, V] // stepper / keyStepper / valueStepper are not overridden to use XTableStepper because that stepper // would not return the elements in insertion order - private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V] private[collection] def _firstEntry: Entry = firstEntry @transient protected var firstEntry: Entry = null + @transient protected var lastEntry: Entry = null - @transient private[this] var table: HashTable[K, V, Entry] = newHashTable - // Used by scala-java8-compat (private[mutable] erases to public, so Java code can access it) - private[mutable] def getTable: HashTable[K, V, Entry] = table + @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) - private def newHashTable = - new HashTable[K, V, Entry] { - def createNewEntry(key: K, value: V): Entry = { - val e = new Entry(key, value) - if (firstEntry eq null) firstEntry = e - else { lastEntry.later = e; e.earlier = lastEntry } - lastEntry = e - e - } + private[this] var threshold: Int = newThreshold(table.length) + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt - override def foreachEntry[U](f: Entry => U): Unit = { - var cur = firstEntry - while (cur ne null) { - f(cur) - cur = cur.later - } - } - - } + private[this] var contentSize = 0 override def last: (K, V) = if (size > 0) (lastEntry.key, lastEntry.value) @@ -117,47 +74,93 @@ class LinkedHashMap[K, V] if (size > 0) Some((firstEntry.key, firstEntry.value)) else None - override def size = table.tableSize + override def size = contentSize override def knownSize: Int = size - override def isEmpty: Boolean = table.tableSize == 0 + override def isEmpty: Boolean = size == 0 def get(key: K): Option[V] = { - val e = table.findEntry(key) + val e = findEntry(key) if (e == null) None else Some(e.value) } override def contains(key: K): Boolean = { if (getClass eq classOf[LinkedHashMap[_, _]]) - table.findEntry(key) != null + findEntry(key) != null else super.contains(key) // A subclass might override `get`, use the default implementation `contains`. } override def put(key: K, value: V): Option[V] = { - val e = table.findOrAddEntry(key, value) - if (e eq null) None - else { val v = e.value; e.value = value; Some(v) } + put0(key, value, true) match { + case null => None + case sm => sm + } } override def update(key: K, value: V): Unit = { - val e = table.findOrAddEntry(key, value) - if (e ne null) e.value = value + put0(key, value, false) + } override def remove(key: K): Option[V] = { - val e = table.removeEntry(key) - if (e eq null) None - else Some(remove0(e)) + removeEntry0(key) match { + case null => None + case nd => Some(nd.value) + } } - private[this] def remove0(e: Entry): V = { - if (e.earlier eq null) firstEntry = e.later - else e.earlier.later = e.later - if (e.later eq null) lastEntry = e.earlier - else e.later.earlier = e.earlier - e.earlier = null // Null references to prevent nepotism - e.later = null - e.value + private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) + + /** Removes a key from this map if it exists + * + * @param elem the element to remove + * @param hash the **improved** hashcode of `element` (see computeHash) + * @return the node that contained element if it was present, otherwise null + */ + private[this] def removeEntry0(elem: K, hash: Int): Entry = { + val idx = index(hash) + table(idx) match { + case null => null + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + deleteEntry(nd) + contentSize -= 1 + nd + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while ((next ne null) && next.hash <= hash) { + if (next.hash == hash && next.key == elem) { + prev.next = next.next + deleteEntry(next) + contentSize -= 1 + return next + } + prev = next + next = next.next + } + null + } + } + + /** Computes the improved hash of an original (`any.##`) hash. */ + @`inline` private[this] def improveHash(originalHash: Int): Int = { + originalHash ^ (originalHash >>> 16) + } + + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: K): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + @`inline` private[this] def findEntry(key: K): Entry = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findEntry(key, hash) + } } def addOne(kv: (K, V)): this.type = { put(kv._1, kv._2); this } @@ -189,28 +192,53 @@ class LinkedHashMap[K, V] // Override updateWith for performance, so we can do the update while hashing // the input key only once and performing one lookup into the hash table override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { - val keyIndex = table.index(table.elemHashCode(key)) - val entry = table.findEntry0(key, keyIndex) + val hash = computeHash(key) + val indexedHash = index(hash) + + var foundEntry = null.asInstanceOf[Entry] + var previousEntry = null.asInstanceOf[Entry] + table(indexedHash) match { + case null => + case nd => + @tailrec + def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { + if (h == nd.hash && k == nd.key) { + previousEntry = prev + foundEntry = nd + } + else if ((nd.next eq null) || (nd.hash > h)) () + else findEntry(nd, nd.next, k, h) + } - val previousValue = - if (entry == null) None - else Some(entry.value) + findEntry(null, nd, key, hash) + } + + val previousValue = foundEntry match { + case null => None + case nd => Some(nd.value) + } val nextValue = remappingFunction(previousValue) (previousValue, nextValue) match { case (None, None) => // do nothing + case (Some(_), None) => - remove0(entry) - table.removeEntry0(key, keyIndex) + if (previousEntry != null) previousEntry.next = foundEntry.next + else table(indexedHash) = foundEntry.next + deleteEntry(foundEntry) + contentSize -= 1 case (None, Some(value)) => - table.addEntry0(table.createNewEntry(key, value), keyIndex) - - case (Some(_), Some(value)) => - entry.value = value + val newIndexedHash = + if (contentSize + 1 >= threshold) { + growTable(table.length * 2) + index(hash) + } else indexedHash + put0(key, value, false, hash, newIndexedHash) + + case (Some(_), Some(newValue)) => foundEntry.value = newValue } - nextValue } @@ -239,26 +267,210 @@ class LinkedHashMap[K, V] } override def clear(): Unit = { - table.clearTable() + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 firstEntry = null lastEntry = null } + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + + /*create a new entry. If table is empty(firstEntry is null), then the + * new entry will be the firstEntry. If not, just set the new entry to + * be the lastEntry. + * */ + private[this] def createNewEntry(key: K, hash: Int, value: V): Entry = + { + val e = new Entry(key, hash, value) + if (firstEntry eq null) firstEntry = e + else { lastEntry.later = e; e.earlier = lastEntry } + lastEntry = e + e + } + + /*delete the entry from the linkedhashmap. set its earlier entry's later entry + * and later entry's earlier entry correctly.and set its earlier and later to + * be null.*/ + private[this] def deleteEntry(e: Entry): Unit = { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null // Null references to prevent nepotism + e.later = null + } + + private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { + if(contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(key) + val idx = index(hash) + put0(key, value, getOld, hash, idx) + } + + private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { + table(idx) match { + case null => + val nnode = createNewEntry(key, hash, value) + nnode.next = null + table(idx) = nnode + case old => + var prev = null.asInstanceOf[Entry] + var n = old + while((n ne null) && n.hash <= hash) { + if(n.hash == hash && key == n.key) { + val old = n.value + n.value = value + return if(getOld) Some(old) else null + } + prev = n + n = n.next + } + val nnode = createNewEntry(key, hash, value) + if(prev eq null) { + table(idx) = nnode + nnode.next = old + } + else { + nnode.next = prev.next + prev.next = nnode} + } + contentSize += 1 + null + } + + private[this] def growTable(newlen: Int): Unit = { + if (newlen < 0) + throw new RuntimeException(s"new hash table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if (size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow = new Entry(null.asInstanceOf[K], 0, null.asInstanceOf[V]) + val preHigh = new Entry(null.asInstanceOf[K], 0, null.asInstanceOf[V]) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while (oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if (old ne null) { + preLow.next = null + preHigh.next = null + var lastLow = preLow + var lastHigh = preHigh + var n = old + while (n ne null) { + val next = n.next + if ((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if (old ne preLow.next) table(i) = preLow.next + if (preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + private[this] def serializeTo(out: java.io.ObjectOutputStream, writeEntry: Entry => Unit): Unit = { + out.writeInt(contentSize) + var cur = firstEntry + while (cur ne null) { + writeEntry(cur) + cur = cur.later + } + } + private def writeObject(out: java.io.ObjectOutputStream): Unit = { out.defaultWriteObject() - table.serializeTo(out, { entry => + serializeTo(out, { entry => out.writeObject(entry.key) out.writeObject(entry.value) }) } + private[this] def serializeFrom(in: java.io.ObjectInputStream, readEntry: => (K, V)): Unit = { + val _contentsize = in.readInt() + assert(_contentsize > 0) + clear() + table = new Array(tableSizeFor(_contentsize)) + threshold = newThreshold(table.length) + + var index = 0 + while (index < size) { + addOne(readEntry) + index += 1 + } + } private def readObject(in: java.io.ObjectInputStream): Unit = { in.defaultReadObject() - table = newHashTable - table.init(in, table.createNewEntry(in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V])) + serializeFrom(in, (in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V])) } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "LinkedHashMap" } +/** $factoryInfo + * @define Coll `LinkedHashMap` + * @define coll linked hash map + */ +@SerialVersionUID(3L) +object LinkedHashMap extends MapFactory[LinkedHashMap] { + + def empty[K, V] = new LinkedHashMap[K, V] + + def from[K, V](it: collection.IterableOnce[(K, V)]) = + it match { + case lhm: LinkedHashMap[K, V] => lhm + case _ => Growable.from(empty[K, V], it) + } + + def newBuilder[K, V] = new GrowableBuilder(empty[K, V]) + + /** Class for the linked hash map entry, used internally. + */ + private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) + { + var earlier: LinkedEntry[K, V] = null + var later: LinkedEntry[K, V] = null + var next: LinkedEntry[K, V] = null + + @tailrec + final def findEntry(k: K, h: Int): LinkedEntry[K, V] = + if (h == hash && k == key) this + else if ((next eq null) || (hash > h)) null + else next.findEntry(k, h) + + @tailrec + final def foreach[U](f: ((K, V)) => U): Unit = { + f((key, value)) + if (next ne null) next.foreach(f) + } + + @tailrec + final def foreachEntry[U](f: (K, V) => U): Unit = { + f(key, value) + if (next ne null) next.foreachEntry(f) + } + + } + + private[collection] final def defaultLoadFactor: Double = 0.75 // corresponds to 75% + /** The default initial capacity for the hash table */ + private[collection] final def defaultinitialSize: Int = 16 +} diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index 3c190a141dd6..e8d8362c30ad 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -14,7 +14,7 @@ package scala package collection package mutable -import scala.annotation.nowarn +import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable /** This class implements mutable sets using a hashtable. @@ -44,29 +44,16 @@ class LinkedHashSet[A] type Entry = LinkedHashSet.Entry[A] @transient protected var firstEntry: Entry = null + @transient protected var lastEntry: Entry = null - @transient private[this] var table: HashTable[A, AnyRef, Entry] = newHashTable - // Used by scala-java8-compat (private[mutable] erases to public, so Java code can access it) - private[mutable] def getTable: HashTable[A, AnyRef, Entry] = table + @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) - private def newHashTable = - new HashTable[A, AnyRef, Entry] { - def createNewEntry(key: A, value: AnyRef) = { - val e = new Entry(key) - if (firstEntry eq null) firstEntry = e - else { lastEntry.later = e; e.earlier = lastEntry } - lastEntry = e - e - } - override def foreachEntry[U](f: Entry => U): Unit = { - var cur = firstEntry - while (cur ne null) { - f(cur) - cur = cur.later - } - } - } + private[this] var threshold: Int = newThreshold(table.length) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashSet.defaultLoadFactor).toInt + + private[this] var contentSize = 0 override def last: A = if (size > 0) lastEntry.key @@ -84,13 +71,17 @@ class LinkedHashSet[A] if (size > 0) Some(firstEntry.key) else None - override def size: Int = table.tableSize + override def size: Int = contentSize + override def knownSize: Int = size + override def isEmpty: Boolean = size == 0 - def contains(elem: A): Boolean = table.findEntry(elem) ne null + + def contains(elem: A): Boolean = findEntry(elem) ne null def addOne(elem: A): this.type = { - table.findOrAddEntry(elem, null) + if(contentSize + 1 >= threshold) growTable(table.length * 2) + addElem(elem, computeHash(elem)) this } @@ -100,17 +91,7 @@ class LinkedHashSet[A] } override def remove(elem: A): Boolean = { - val e = table.removeEntry(elem) - if (e eq null) false - else { - if (e.earlier eq null) firstEntry = e.later - else e.earlier.later = e.later - if (e.later eq null) lastEntry = e.earlier - else e.later.earlier = e.earlier - e.earlier = null // Null references to prevent nepotism - e.later = null - true - } + remove0(elem, computeHash(elem)) } def iterator: Iterator[A] = new AbstractIterator[A] { @@ -130,20 +111,199 @@ class LinkedHashSet[A] } override def clear(): Unit = { - table.clearTable() + java.util.Arrays.fill(table.asInstanceOf[Array[AnyRef]], null) + contentSize = 0 firstEntry = null lastEntry = null } + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + + @`inline` private[this] def improveHash(originalHash: Int): Int = { + originalHash ^ (originalHash >>> 16) + } + + /** Computes the improved hash of this key */ + @`inline` private[this] def computeHash(o: A): Int = improveHash(o.##) + + @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) + + @`inline` private[this] def findEntry(key: A): Entry = { + val hash = computeHash(key) + table(index(hash)) match { + case null => null + case nd => nd.findEntry(key, hash) + } + } + + /*create a new entry. If table is empty(firstEntry is null), then the + * new entry will be the firstEntry. If not, just set the new entry to + * be the lastEntry. + * */ + private[this] def createNewEntry(key: A, hash: Int): Entry = + { + val e = new Entry(key, hash) + if (firstEntry eq null) firstEntry = e + else { lastEntry.later = e; e.earlier = lastEntry } + lastEntry = e + e + } + + /*delete the entry from the linkedhashset. set its earlier entry's later entry + * and later entry's earlier entry correctly.and then set its earlier and later + * to be null.*/ + private[this] def deleteEntry(e: Entry): Unit = + { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null // Null references to prevent nepotism + e.later = null + } + + /** Adds an element to this set + * @param elem element to add + * @param hash the **improved** hash of `elem` (see computeHash) + */ + private[this] def addElem(elem: A, hash: Int) : Boolean = { + val idx = index(hash) + table(idx) match { + case null => + val nnode = createNewEntry(elem, hash) + table(idx) = nnode + nnode.next = null + case old => + var prev = null.asInstanceOf[Entry] + var n = old + while((n ne null) && n.hash <= hash) { + if(n.hash == hash && elem == n.key) return false + prev = n + n = n.next + } + val nnode = createNewEntry(elem, hash) + if(prev eq null) { + nnode.next = old + table(idx) = nnode + + } else { + nnode.next = prev.next + prev.next = nnode + } + } + contentSize += 1 + true + } + + private[this] def remove0(elem: A, hash: Int): Boolean = { + val idx = index(hash) + table(idx) match { + case null => false + case nd if nd.hash == hash && nd.key == elem => + // first element matches + table(idx) = nd.next + deleteEntry(nd) + nd.next = null + contentSize -= 1 + true + case nd => + // find an element that matches + var prev = nd + var next = nd.next + while((next ne null) && next.hash <= hash) { + if(next.hash == hash && next.key == elem) { + prev.next = next.next + deleteEntry(next) + next.next = null + contentSize -= 1 + return true + } + prev = next + next = next.next + } + false + } + } + + private[this] def growTable(newlen: Int) = { + if (newlen < 0) + throw new RuntimeException(s"new hash table size $newlen exceeds maximum") + var oldlen = table.length + threshold = newThreshold(newlen) + if (size == 0) table = new Array(newlen) + else { + table = java.util.Arrays.copyOf(table, newlen) + val preLow = new Entry(null.asInstanceOf[A], 0) + val preHigh = new Entry(null.asInstanceOf[A], 0) + // Split buckets until the new length has been reached. This could be done more + // efficiently when growing an already filled table to more than double the size. + while (oldlen < newlen) { + var i = 0 + while (i < oldlen) { + val old = table(i) + if (old ne null) { + preLow.next = null + preHigh.next = null + var lastLow = preLow + var lastHigh = preHigh + var n = old + while (n ne null) { + val next = n.next + if ((n.hash & oldlen) == 0) { // keep low + lastLow.next = n + lastLow = n + } else { // move to high + lastHigh.next = n + lastHigh = n + } + n = next + } + lastLow.next = null + if (old ne preLow.next) table(i) = preLow.next + if (preHigh.next ne null) { + table(i + oldlen) = preHigh.next + lastHigh.next = null + } + } + i += 1 + } + oldlen *= 2 + } + } + } + + private[this] def serializeTo(out: java.io.ObjectOutputStream, writeEntry: Entry => Unit): Unit = { + out.writeInt(contentSize) + var cur = firstEntry + while (cur ne null) { + writeEntry(cur) + cur = cur.later + } + } + private def writeObject(out: java.io.ObjectOutputStream): Unit = { out.defaultWriteObject() - table.serializeTo(out, { e => out.writeObject(e.key) }) + serializeTo(out, { e => out.writeObject(e.key) }) + } + + private[this] def serializeFrom(in: java.io.ObjectInputStream, readEntry: => A): Unit = { + val _contentsize = in.readInt() + assert(_contentsize > 0) + clear() + table = new Array(tableSizeFor(_contentsize)) + threshold = newThreshold(table.length) + + var index = 0 + while (index < size) { + addOne(readEntry) + index += 1 + } } private def readObject(in: java.io.ObjectInputStream): Unit = { in.defaultReadObject() - table = newHashTable - table.init(in, table.createNewEntry(in.readObject().asInstanceOf[A], null)) + serializeFrom(in, in.readObject().asInstanceOf[A]) } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") @@ -169,9 +329,33 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { /** Class for the linked hash set entry, used internally. */ - private[mutable] final class Entry[A](val key: A) extends HashEntry[A, Entry[A]] { + private[mutable] final class Entry[A](val key: A, val hash: Int) { var earlier: Entry[A] = null var later: Entry[A] = null + var next: Entry[A] = null + + @tailrec + final def findEntry(k: A, h: Int): Entry[A] = + if (h == hash && k == key) this + else if ((next eq null) || (hash > h)) null + else next.findEntry(k, h) + + @tailrec + final def foreach[U](f: A => U): Unit = { + f(key) + if (next ne null) next.foreach(f) + } + + @tailrec + final def foreachEntry[U](f: A => U): Unit = { + f(key) + if (next ne null) next.foreachEntry(f) + } } + + private[collection] final def defaultLoadFactor: Double = 0.75 // corresponds to 75% + + /** The default initial capacity for the hash table */ + private[collection] final def defaultinitialSize: Int = 16 } diff --git a/test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala b/test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala new file mode 100644 index 000000000000..6725141f3b41 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/mutable/LinkedHashMapBenchmark2.scala @@ -0,0 +1,216 @@ +package scala.collection.mutable + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra._ +import org.openjdk.jmh.runner.IterationType +import benchmark._ +import java.util.concurrent.TimeUnit +import java.util.{ LinkedHashMap => JLHashMap, LinkedHashSet => JLHashSet } + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 20) +@Measurement(iterations = 20) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class LinkedHashMapBenchmark2 { + @Param(Array(/*"0", "1",*/ "10", "100", "1000", "10000")) + var size: Int = _ + @Param(Array("true")) + var stringsOnly = false + + class Collider(val x: Any, val h: Int) { + override def hashCode: Int = h + override def equals(o: Any): Boolean = o match { + case o: Collider => x == o.x + case _ => false + } + } + + var existingKeys: Array[Any] = _ + var existingKVs: ArrayBuffer[(Any, Any)] = _ + var missingKeys: Array[Any] = _ + var s1: LinkedHashSet[Any] = _ + var m1: LinkedHashMap[Any, Any] = _ + var j1: JLHashMap[Any, Any] = new JLHashMap[Any, Any] + var j2: JLHashSet[Any] = new JLHashSet[Any] + var colliders: Array[Collider] = _ + + @Setup(Level.Trial) def init: Unit = { + existingKeys = (0 until size).map(i => (i % 4) match { + case _ if stringsOnly => i.toString + case 0 => i.toString + case 1 => i.toChar + case 2 => i.toDouble + case 3 => i.toInt + }).toArray + existingKVs = ArrayBuffer.from(existingKeys.iterator.map(k => (k, k))) + missingKeys = (size until (2 * size.max(100))).toArray.map(_.toString) + s1 = LinkedHashSet.from(existingKeys) + m1 = LinkedHashMap.from(existingKVs) + m1.foreach { case (k, v) => j1.put(k, v) } + s1.foreach({case k => j2.add(k)}) + colliders = existingKeys.map(k => new Collider(k, k.hashCode & 0x1111)) + } + + @Benchmark def lhsFillRegular(bh: Blackhole): Unit = { + val h = new LinkedHashSet[Any] + existingKeys.foreach(k => h.addOne(k)) + bh.consume(h) + } + + @Benchmark def lhsFillColliding(bh: Blackhole): Unit = { + val h = new LinkedHashSet[Any] + colliders.foreach(k => h.addOne(k)) + bh.consume(h) + } + + @Benchmark def lhsBuild(bh: Blackhole): Unit = + bh.consume(LinkedHashSet.from(existingKeys)) + + @Benchmark def lhsIterate(bh: Blackhole): Unit = { + val it = s1.iterator + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def lhsContainsTrue(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(s1.contains(existingKeys(i))) + i += 1 + } + } + + @Benchmark def lhsContainsFalse(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(s1.contains(missingKeys(i))) + i += 1 + } + } + + @Benchmark def lhmFillRegular(bh: Blackhole): Unit = { + val h = new LinkedHashMap[Any, Any] + existingKeys.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def lhmFillColliding(bh: Blackhole): Unit = { + val h = new LinkedHashMap[Any, Any] + colliders.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def lhmBuild(bh: Blackhole): Unit = + bh.consume(LinkedHashMap.from(existingKVs)) + + @Benchmark def lhmIterateKeys(bh: Blackhole): Unit = { + val it = m1.keysIterator + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def lhmIterateEntries(bh: Blackhole): Unit = { + val it = m1.iterator + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def lhmGetExisting(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(m1.apply(existingKeys(i))) + i += 1 + } + } + + @Benchmark def lhmGetNone(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(m1.get(missingKeys(i))) + i += 1 + } + } + + @Benchmark def javalhmFillRegular(bh: Blackhole): Unit = { + val h = new JLHashMap[Any, Any] + existingKeys.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def javalhmFillColliding(bh: Blackhole): Unit = { + val h = new JLHashMap[Any, Any] + colliders.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def javalhmBuild(bh: Blackhole): Unit = { + val h = new JLHashMap[Any, Any](((existingKeys.length+1).toDouble/0.75).toInt, 0.75f) + existingKeys.foreach(k => h.put(k, k)) + bh.consume(h) + } + + @Benchmark def javalhmIterateKeys(bh: Blackhole): Unit = { + val it = j1.keySet().iterator() + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def javalhmIterateEntries(bh: Blackhole): Unit = { + val it = j1.entrySet().iterator() + while(it.hasNext) bh.consume(it.next()) + } + + @Benchmark def javalhmGetExisting(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(j1.get(existingKeys(i))) + i += 1 + } + } + + @Benchmark def javalhmGetNone(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(j1.get(missingKeys(i))) + i += 1 + } + } + @Benchmark def javalhsFillRegular(bh: Blackhole): Unit = { + val h = new JLHashSet[Any] + existingKeys.foreach(k => h.add(k)) + bh.consume(h) + } + + @Benchmark def javalhsFillColliding(bh: Blackhole): Unit = { + val h = new JLHashSet[Any] + colliders.foreach(k => h.add(k)) + bh.consume(h) + } + + @Benchmark def javalhsBuild(bh: Blackhole): Unit = { + val h = new JLHashSet[Any](((existingKeys.length+1).toDouble/0.75).toInt, 0.75f) + existingKeys.foreach(k => h.add(k)) + bh.consume(h) + } + + @Benchmark def javalhsIterate(bh: Blackhole): Unit = { + val it = j2.iterator() + while(it.hasNext) bh.consume(it.next()) + } + + + @Benchmark def javalhsContainsTrue(bh: Blackhole): Unit = { + var i = 0 + while (i < size) { + bh.consume(j2.contains(existingKeys(i))) + i += 1 + } + } + + @Benchmark def javalhsContainsFalse(bh: Blackhole): Unit = { + var i = 0 + while (i < size.max(100)) { + bh.consume(j2.contains(missingKeys(i))) + i += 1 + } + } +} From c22150966cf9e9ae5c727da1fa2f20617d067345 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 25 Nov 2022 16:01:10 +0100 Subject: [PATCH 091/261] LinkedHashMap/LinkedHashSet: review cleanups * Add `LinkedHashSet.add` override * Mark the private[collection] HashTable as not used --- .../scala/tools/nsc/interactive/Global.scala | 1 + .../scala/collection/mutable/HashTable.scala | 4 +- .../collection/mutable/LinkedHashMap.scala | 115 +++++++++-------- .../collection/mutable/LinkedHashSet.scala | 118 ++++++++---------- .../collection/mutable/OpenHashMap.scala | 6 +- .../mutable/LinkedHashMapTest.scala | 4 +- .../mutable/LinkedHashSetTest.scala | 4 +- 7 files changed, 121 insertions(+), 131 deletions(-) diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 327da6359c5f..9fe850139525 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -995,6 +995,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") respond(response) { scopeMemberFlatten(scopeMembers(pos)) } } + @nowarn("msg=inheritance from class LinkedHashMap") private class Members[M <: Member] extends LinkedHashMap[Name, Set[M]] { override def default(key: Name) = Set() diff --git a/src/library/scala/collection/mutable/HashTable.scala b/src/library/scala/collection/mutable/HashTable.scala index dc559e86ce98..4153bd532163 100644 --- a/src/library/scala/collection/mutable/HashTable.scala +++ b/src/library/scala/collection/mutable/HashTable.scala @@ -35,8 +35,8 @@ import java.lang.Integer * * @tparam A type of the elements contained in this hash table. */ -// Was an abstract class, but to simplify the upgrade of the parallel collections I’ve made it a trait -private[collection] /*abstract class*/ trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { +// Not used in the standard library, but used in scala-parallel-collections +private[collection] trait HashTable[A, B, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashUtils[A] { // Replacing Entry type parameter by abstract type member here allows to not expose to public // implementation-specific entry classes such as `DefaultEntry` or `LinkedEntry`. // However, I'm afraid it's too late now for such breaking change. diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index ddc039ef6bd8..1e4681d5d998 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -31,6 +31,7 @@ import scala.collection.generic.DefaultSerializable * @define orderDependent * @define orderDependentFold */ +@deprecatedInheritance("LinkedHashMap will be made final; use .withDefault for the common use case of computing a default value", "2.13.11") class LinkedHashMap[K, V] extends AbstractMap[K, V] with SeqMap[K, V] @@ -44,17 +45,23 @@ class LinkedHashMap[K, V] // stepper / keyStepper / valueStepper are not overridden to use XTableStepper because that stepper // would not return the elements in insertion order + private[collection] type Entry = LinkedHashMap.LinkedEntry[K, V] + private[collection] def _firstEntry: Entry = firstEntry @transient protected var firstEntry: Entry = null @transient protected var lastEntry: Entry = null - @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) + /* Uses the same implementation as mutable.HashMap. The hashtable holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendant hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ + @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) private[this] var threshold: Int = newThreshold(table.length) - private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt private[this] var contentSize = 0 @@ -77,6 +84,7 @@ class LinkedHashMap[K, V] override def size = contentSize override def knownSize: Int = size override def isEmpty: Boolean = size == 0 + def get(key: K): Option[V] = { val e = findEntry(key) if (e == null) None @@ -90,23 +98,16 @@ class LinkedHashMap[K, V] super.contains(key) // A subclass might override `get`, use the default implementation `contains`. } - override def put(key: K, value: V): Option[V] = { - put0(key, value, true) match { + override def put(key: K, value: V): Option[V] = put0(key, value, true) match { case null => None case sm => sm } - } - - override def update(key: K, value: V): Unit = { - put0(key, value, false) - } + override def update(key: K, value: V): Unit = put0(key, value, false) - override def remove(key: K): Option[V] = { - removeEntry0(key) match { + override def remove(key: K): Option[V] = removeEntry0(key) match { case null => None case nd => Some(nd.value) - } } private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) @@ -155,7 +156,7 @@ class LinkedHashMap[K, V] @`inline` private[this] def index(hash: Int) = hash & (table.length - 1) - @`inline` private[this] def findEntry(key: K): Entry = { + @`inline` private[this] def findEntry(key: K): Entry = { val hash = computeHash(key) table(index(hash)) match { case null => null @@ -163,9 +164,15 @@ class LinkedHashMap[K, V] } } - def addOne(kv: (K, V)): this.type = { put(kv._1, kv._2); this } + def addOne(kv: (K, V)): this.type = { + put(kv._1, kv._2) + this + } - def subtractOne(key: K): this.type = { remove(key); this } + def subtractOne(key: K): this.type = { + remove(key) + this + } def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] { private[this] var cur = firstEntry @@ -195,8 +202,8 @@ class LinkedHashMap[K, V] val hash = computeHash(key) val indexedHash = index(hash) - var foundEntry = null.asInstanceOf[Entry] - var previousEntry = null.asInstanceOf[Entry] + var foundEntry: Entry = null + var previousEntry: Entry = null table(indexedHash) match { case null => case nd => @@ -273,36 +280,39 @@ class LinkedHashMap[K, V] lastEntry = null } - private[this] def tableSizeFor(capacity: Int) = - (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + private[this] def tableSizeFor(capacity: Int) = + (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashMap.defaultLoadFactor).toInt /*create a new entry. If table is empty(firstEntry is null), then the * new entry will be the firstEntry. If not, just set the new entry to * be the lastEntry. * */ - private[this] def createNewEntry(key: K, hash: Int, value: V): Entry = - { - val e = new Entry(key, hash, value) - if (firstEntry eq null) firstEntry = e - else { lastEntry.later = e; e.earlier = lastEntry } - lastEntry = e - e + private[this] def createNewEntry(key: K, hash: Int, value: V): Entry = { + val e = new Entry(key, hash, value) + if (firstEntry eq null) firstEntry = e + else { + lastEntry.later = e + e.earlier = lastEntry } + lastEntry = e + e + } - /*delete the entry from the linkedhashmap. set its earlier entry's later entry - * and later entry's earlier entry correctly.and set its earlier and later to - * be null.*/ + /** Delete the entry from the LinkedHashMap, set the `earlier` and `later` pointers correctly */ private[this] def deleteEntry(e: Entry): Unit = { if (e.earlier eq null) firstEntry = e.later else e.earlier.later = e.later if (e.later eq null) lastEntry = e.earlier else e.later.earlier = e.earlier - e.earlier = null // Null references to prevent nepotism + e.earlier = null e.later = null + e.next = null } private[this] def put0(key: K, value: V, getOld: Boolean): Some[V] = { - if(contentSize + 1 >= threshold) growTable(table.length * 2) + if (contentSize + 1 >= threshold) growTable(table.length * 2) val hash = computeHash(key) val idx = index(hash) put0(key, value, getOld, hash, idx) @@ -311,29 +321,27 @@ class LinkedHashMap[K, V] private[this] def put0(key: K, value: V, getOld: Boolean, hash: Int, idx: Int): Some[V] = { table(idx) match { case null => - val nnode = createNewEntry(key, hash, value) - nnode.next = null - table(idx) = nnode + table(idx) = createNewEntry(key, hash, value) case old => - var prev = null.asInstanceOf[Entry] + var prev: Entry = null var n = old - while((n ne null) && n.hash <= hash) { - if(n.hash == hash && key == n.key) { + while ((n ne null) && n.hash <= hash) { + if (n.hash == hash && key == n.key) { val old = n.value n.value = value - return if(getOld) Some(old) else null + return if (getOld) Some(old) else null } prev = n n = n.next } val nnode = createNewEntry(key, hash, value) - if(prev eq null) { - table(idx) = nnode + if (prev eq null) { nnode.next = old - } - else { + table(idx) = nnode + } else { nnode.next = prev.next - prev.next = nnode} + prev.next = nnode + } } contentSize += 1 null @@ -416,6 +424,7 @@ class LinkedHashMap[K, V] index += 1 } } + private def readObject(in: java.io.ObjectInputStream): Unit = { in.defaultReadObject() serializeFrom(in, (in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V])) @@ -444,8 +453,7 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] { /** Class for the linked hash map entry, used internally. */ - private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) - { + private[mutable] final class LinkedEntry[K, V](val key: K, val hash: Int, var value: V) { var earlier: LinkedEntry[K, V] = null var later: LinkedEntry[K, V] = null var next: LinkedEntry[K, V] = null @@ -455,22 +463,11 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] { if (h == hash && k == key) this else if ((next eq null) || (hash > h)) null else next.findEntry(k, h) - - @tailrec - final def foreach[U](f: ((K, V)) => U): Unit = { - f((key, value)) - if (next ne null) next.foreach(f) - } - - @tailrec - final def foreachEntry[U](f: (K, V) => U): Unit = { - f(key, value) - if (next ne null) next.foreachEntry(f) - } - } - private[collection] final def defaultLoadFactor: Double = 0.75 // corresponds to 75% + /** The default load factor for the hash table */ + private[collection] final def defaultLoadFactor: Double = 0.75 + /** The default initial capacity for the hash table */ private[collection] final def defaultinitialSize: Int = 16 } diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index e8d8362c30ad..d1e0dd2e7fa2 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -29,12 +29,13 @@ import scala.collection.generic.DefaultSerializable * @define orderDependent * @define orderDependentFold */ +@deprecatedInheritance("LinkedHashSet will be made final", "2.13.11") class LinkedHashSet[A] extends AbstractSet[A] with SetOps[A, LinkedHashSet, LinkedHashSet[A]] with StrictOptimizedIterableOps[A, LinkedHashSet, LinkedHashSet[A]] with IterableFactoryDefaults[A, LinkedHashSet] - with DefaultSerializable { + with DefaultSerializable { override def iterableFactory: IterableFactory[LinkedHashSet] = LinkedHashSet @@ -47,18 +48,21 @@ class LinkedHashSet[A] @transient protected var lastEntry: Entry = null + /* Uses the same implementation as mutable.HashSet. The hashtable holds the following invariant: + * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. + * - Every bucket is sorted in ascendant hash order + * - The sum of the lengths of all buckets is equal to contentSize. + */ @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) private[this] var threshold: Int = newThreshold(table.length) - private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashSet.defaultLoadFactor).toInt - private[this] var contentSize = 0 override def last: A = if (size > 0) lastEntry.key else throw new NoSuchElementException("Cannot call .last on empty LinkedHashSet") - + override def lastOption: Option[A] = if (size > 0) Some(lastEntry.key) else None @@ -66,22 +70,25 @@ class LinkedHashSet[A] override def head: A = if (size > 0) firstEntry.key else throw new NoSuchElementException("Cannot call .head on empty LinkedHashSet") - + override def headOption: Option[A] = if (size > 0) Some(firstEntry.key) else None override def size: Int = contentSize - override def knownSize: Int = size - override def isEmpty: Boolean = size == 0 def contains(elem: A): Boolean = findEntry(elem) ne null + override def add(elem: A): Boolean = { + if (contentSize + 1 >= threshold) growTable(table.length * 2) + val hash = computeHash(elem) + put0(elem, hash, index(hash)) + } + def addOne(elem: A): this.type = { - if(contentSize + 1 >= threshold) growTable(table.length * 2) - addElem(elem, computeHash(elem)) + add(elem) this } @@ -90,9 +97,7 @@ class LinkedHashSet[A] this } - override def remove(elem: A): Boolean = { - remove0(elem, computeHash(elem)) - } + override def remove(elem: A): Boolean = remove0(elem, computeHash(elem)) def iterator: Iterator[A] = new AbstractIterator[A] { private[this] var cur = firstEntry @@ -118,7 +123,9 @@ class LinkedHashSet[A] } private[this] def tableSizeFor(capacity: Int) = - (Integer.highestOneBit((capacity-1).max(4))*2).min(1 << 30) + (Integer.highestOneBit((capacity - 1).max(4)) * 2).min(1 << 30) + + private[this] def newThreshold(size: Int) = (size.toDouble * LinkedHashSet.defaultLoadFactor).toInt @`inline` private[this] def improveHash(originalHash: Int): Int = { originalHash ^ (originalHash >>> 16) @@ -141,52 +148,44 @@ class LinkedHashSet[A] * new entry will be the firstEntry. If not, just set the new entry to * be the lastEntry. * */ - private[this] def createNewEntry(key: A, hash: Int): Entry = - { - val e = new Entry(key, hash) - if (firstEntry eq null) firstEntry = e - else { lastEntry.later = e; e.earlier = lastEntry } - lastEntry = e - e + private[this] def createNewEntry(key: A, hash: Int): Entry = { + val e = new Entry(key, hash) + if (firstEntry eq null) firstEntry = e + else { + lastEntry.later = e + e.earlier = lastEntry } + lastEntry = e + e + } - /*delete the entry from the linkedhashset. set its earlier entry's later entry - * and later entry's earlier entry correctly.and then set its earlier and later - * to be null.*/ - private[this] def deleteEntry(e: Entry): Unit = - { - if (e.earlier eq null) firstEntry = e.later - else e.earlier.later = e.later - if (e.later eq null) lastEntry = e.earlier - else e.later.earlier = e.earlier - e.earlier = null // Null references to prevent nepotism - e.later = null - } + /** Delete the entry from the LinkedHashSet, set the `earlier` and `later` pointers correctly */ + private[this] def deleteEntry(e: Entry): Unit = { + if (e.earlier eq null) firstEntry = e.later + else e.earlier.later = e.later + if (e.later eq null) lastEntry = e.earlier + else e.later.earlier = e.earlier + e.earlier = null + e.later = null + e.next = null + } - /** Adds an element to this set - * @param elem element to add - * @param hash the **improved** hash of `elem` (see computeHash) - */ - private[this] def addElem(elem: A, hash: Int) : Boolean = { - val idx = index(hash) + private[this] def put0(elem: A, hash: Int, idx: Int): Boolean = { table(idx) match { case null => - val nnode = createNewEntry(elem, hash) - table(idx) = nnode - nnode.next = null + table(idx) = createNewEntry(elem, hash) case old => - var prev = null.asInstanceOf[Entry] + var prev: Entry = null var n = old - while((n ne null) && n.hash <= hash) { - if(n.hash == hash && elem == n.key) return false + while ((n ne null) && n.hash <= hash) { + if (n.hash == hash && elem == n.key) return false prev = n n = n.next } - val nnode = createNewEntry(elem, hash) - if(prev eq null) { + val nnode = createNewEntry(elem, hash) + if (prev eq null) { nnode.next = old table(idx) = nnode - } else { nnode.next = prev.next prev.next = nnode @@ -204,18 +203,16 @@ class LinkedHashSet[A] // first element matches table(idx) = nd.next deleteEntry(nd) - nd.next = null contentSize -= 1 true case nd => // find an element that matches var prev = nd var next = nd.next - while((next ne null) && next.hash <= hash) { - if(next.hash == hash && next.key == elem) { + while ((next ne null) && next.hash <= hash) { + if (next.hash == hash && next.key == elem) { prev.next = next.next deleteEntry(next) - next.next = null contentSize -= 1 return true } @@ -226,7 +223,7 @@ class LinkedHashSet[A] } } - private[this] def growTable(newlen: Int) = { + private[this] def growTable(newlen: Int): Unit = { if (newlen < 0) throw new RuntimeException(s"new hash table size $newlen exceeds maximum") var oldlen = table.length @@ -329,7 +326,7 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { /** Class for the linked hash set entry, used internally. */ - private[mutable] final class Entry[A](val key: A, val hash: Int) { + private[mutable] final class Entry[A](val key: A, val hash: Int) { var earlier: Entry[A] = null var later: Entry[A] = null var next: Entry[A] = null @@ -339,21 +336,10 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { if (h == hash && k == key) this else if ((next eq null) || (hash > h)) null else next.findEntry(k, h) - - @tailrec - final def foreach[U](f: A => U): Unit = { - f(key) - if (next ne null) next.foreach(f) - } - - @tailrec - final def foreachEntry[U](f: A => U): Unit = { - f(key) - if (next ne null) next.foreachEntry(f) - } } - private[collection] final def defaultLoadFactor: Double = 0.75 // corresponds to 75% + /** The default load factor for the hash table */ + private[collection] final def defaultLoadFactor: Double = 0.75 /** The default initial capacity for the hash table */ private[collection] final def defaultinitialSize: Int = 16 diff --git a/src/library/scala/collection/mutable/OpenHashMap.scala b/src/library/scala/collection/mutable/OpenHashMap.scala index 481317337663..22e99d4650d1 100644 --- a/src/library/scala/collection/mutable/OpenHashMap.scala +++ b/src/library/scala/collection/mutable/OpenHashMap.scala @@ -13,8 +13,8 @@ package scala.collection package mutable +import java.lang.Integer.numberOfLeadingZeros import java.util.ConcurrentModificationException - import scala.collection.generic.DefaultSerializable /** @@ -41,6 +41,8 @@ object OpenHashMap extends MapFactory[OpenHashMap] { final private class OpenEntry[Key, Value](var key: Key, var hash: Int, var value: Option[Value]) + + private[mutable] def nextPositivePowerOfTwo(target: Int): Int = 1 << -numberOfLeadingZeros(target - 1) } /** A mutable hash map based on an open addressing method. The precise scheme is @@ -75,7 +77,7 @@ class OpenHashMap[Key, Value](initialSize : Int) override def mapFactory: MapFactory[OpenHashMap] = OpenHashMap - private[this] val actualInitialSize = HashTable.nextPositivePowerOfTwo(initialSize) + private[this] val actualInitialSize = OpenHashMap.nextPositivePowerOfTwo(initialSize) private[this] var mask = actualInitialSize - 1 diff --git a/test/junit/scala/collection/mutable/LinkedHashMapTest.scala b/test/junit/scala/collection/mutable/LinkedHashMapTest.scala index 5b76a43cb427..6fb26e88a5d6 100644 --- a/test/junit/scala/collection/mutable/LinkedHashMapTest.scala +++ b/test/junit/scala/collection/mutable/LinkedHashMapTest.scala @@ -3,13 +3,15 @@ package scala.collection.mutable import org.junit.Assert._ import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.junit.{ Assert, Test } +import org.junit.{Assert, Test} +import scala.annotation.nowarn import scala.collection.mutable /* Test for scala/bug#9095 */ @RunWith(classOf[JUnit4]) class LinkedHashMapTest { + @nowarn("msg=inheritance from class LinkedHashMap") class TestClass extends mutable.LinkedHashMap[String, Int] { def lastItemRef = lastEntry } diff --git a/test/junit/scala/collection/mutable/LinkedHashSetTest.scala b/test/junit/scala/collection/mutable/LinkedHashSetTest.scala index 92a5de20745d..2ee4c2c14b33 100644 --- a/test/junit/scala/collection/mutable/LinkedHashSetTest.scala +++ b/test/junit/scala/collection/mutable/LinkedHashSetTest.scala @@ -2,13 +2,15 @@ package scala.collection.mutable import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.junit.{ Assert, Test } +import org.junit.{Assert, Test} +import scala.annotation.nowarn import scala.collection.mutable /* Test for scala/bug#9095 */ @RunWith(classOf[JUnit4]) class LinkedHashSetTest { + @nowarn("msg=inheritance from class LinkedHashSet") class TestClass extends mutable.LinkedHashSet[String] { def lastItemRef = lastEntry } From d07b344face50047ac404fc719b69d14a5893156 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 28 Nov 2022 15:39:22 +0100 Subject: [PATCH 092/261] Remove unused serialization-related code `LinkedHashMap/Set` extend `DefaultSerializable`, so instances are not directly serialized, the `DefaultSerializationProxy` is used instead. Serialization-related code in `LinkedHashMap/Set` is unused. --- .../collection/mutable/LinkedHashMap.scala | 42 ++----------------- .../collection/mutable/LinkedHashSet.scala | 39 ++--------------- 2 files changed, 6 insertions(+), 75 deletions(-) diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index 1e4681d5d998..1853ed30759b 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -50,16 +50,16 @@ class LinkedHashMap[K, V] private[collection] def _firstEntry: Entry = firstEntry - @transient protected var firstEntry: Entry = null + protected var firstEntry: Entry = null - @transient protected var lastEntry: Entry = null + protected var lastEntry: Entry = null /* Uses the same implementation as mutable.HashMap. The hashtable holds the following invariant: * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. * - Every bucket is sorted in ascendant hash order * - The sum of the lengths of all buckets is equal to contentSize. */ - @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) + private[this] var table = new Array[Entry](tableSizeFor(LinkedHashMap.defaultinitialSize)) private[this] var threshold: Int = newThreshold(table.length) @@ -394,42 +394,6 @@ class LinkedHashMap[K, V] } } - private[this] def serializeTo(out: java.io.ObjectOutputStream, writeEntry: Entry => Unit): Unit = { - out.writeInt(contentSize) - var cur = firstEntry - while (cur ne null) { - writeEntry(cur) - cur = cur.later - } - } - - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.defaultWriteObject() - serializeTo(out, { entry => - out.writeObject(entry.key) - out.writeObject(entry.value) - }) - } - - private[this] def serializeFrom(in: java.io.ObjectInputStream, readEntry: => (K, V)): Unit = { - val _contentsize = in.readInt() - assert(_contentsize > 0) - clear() - table = new Array(tableSizeFor(_contentsize)) - threshold = newThreshold(table.length) - - var index = 0 - while (index < size) { - addOne(readEntry) - index += 1 - } - } - - private def readObject(in: java.io.ObjectInputStream): Unit = { - in.defaultReadObject() - serializeFrom(in, (in.readObject().asInstanceOf[K], in.readObject().asInstanceOf[V])) - } - @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "LinkedHashMap" } diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index d1e0dd2e7fa2..115ed2fb7e3f 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -44,16 +44,16 @@ class LinkedHashSet[A] type Entry = LinkedHashSet.Entry[A] - @transient protected var firstEntry: Entry = null + protected var firstEntry: Entry = null - @transient protected var lastEntry: Entry = null + protected var lastEntry: Entry = null /* Uses the same implementation as mutable.HashSet. The hashtable holds the following invariant: * - For each i between 0 and table.length, the bucket at table(i) only contains keys whose hash-index is i. * - Every bucket is sorted in ascendant hash order * - The sum of the lengths of all buckets is equal to contentSize. */ - @transient private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) + private[this] var table = new Array[Entry](tableSizeFor(LinkedHashSet.defaultinitialSize)) private[this] var threshold: Int = newThreshold(table.length) @@ -270,39 +270,6 @@ class LinkedHashSet[A] } } - private[this] def serializeTo(out: java.io.ObjectOutputStream, writeEntry: Entry => Unit): Unit = { - out.writeInt(contentSize) - var cur = firstEntry - while (cur ne null) { - writeEntry(cur) - cur = cur.later - } - } - - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.defaultWriteObject() - serializeTo(out, { e => out.writeObject(e.key) }) - } - - private[this] def serializeFrom(in: java.io.ObjectInputStream, readEntry: => A): Unit = { - val _contentsize = in.readInt() - assert(_contentsize > 0) - clear() - table = new Array(tableSizeFor(_contentsize)) - threshold = newThreshold(table.length) - - var index = 0 - while (index < size) { - addOne(readEntry) - index += 1 - } - } - - private def readObject(in: java.io.ObjectInputStream): Unit = { - in.defaultReadObject() - serializeFrom(in, in.readObject().asInstanceOf[A]) - } - @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "LinkedHashSet" } From 7c32d244e1a5cdc88497eaa0829e46ccb0815237 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 2 Dec 2022 10:46:46 +0100 Subject: [PATCH 093/261] Don't catch UnsupportedOperationException in IterableOnce.reduceRight --- src/library/scala/collection/IterableOnce.scala | 7 +------ test/junit/scala/collection/IterableTest.scala | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/library/scala/collection/IterableOnce.scala b/src/library/scala/collection/IterableOnce.scala index a480f8406c46..65d8dce08ae4 100644 --- a/src/library/scala/collection/IterableOnce.scala +++ b/src/library/scala/collection/IterableOnce.scala @@ -783,12 +783,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] => def reduceRight[B >: A](op: (A, B) => B): B = this match { case seq: IndexedSeq[A @unchecked] if seq.length > 0 => foldr[A, B](seq, op) case _ if knownSize == 0 => throw new UnsupportedOperationException("empty.reduceRight") - case _ => - try reversed.reduceLeft[B]((x, y) => op(y, x)) // reduceLeftIterator - catch { - case e: UnsupportedOperationException if e.getMessage == "empty.reduceLeft" => - throw new UnsupportedOperationException("empty.reduceRight") - } + case _ => reversed.reduceLeft[B]((x, y) => op(y, x)) // reduceLeftIterator } /** Optionally applies a binary operator to all elements of this $coll, going left to right. diff --git a/test/junit/scala/collection/IterableTest.scala b/test/junit/scala/collection/IterableTest.scala index bb815d22013f..424c5cc9dfad 100644 --- a/test/junit/scala/collection/IterableTest.scala +++ b/test/junit/scala/collection/IterableTest.scala @@ -463,7 +463,7 @@ class IterableTest { @Test def `IterableOnceOps.reduceRight consumes one iterator`: Unit = assertEquals(106, SingleUseIterable(42, 27, 37).reduceRight(_ + _)) @Test def `IterableOnceOps.reduceRight of empty iterator`: Unit = - assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().reduceRight(_ + _), _.contains("reduceRight")) + assertThrows[UnsupportedOperationException](SingleUseIterable[Int]().reduceRight(_ + _), _.contains("reduceLeft")) @Test def `IterableOnceOps.reduceRight consumes no iterator`: Unit = assertThrows[UnsupportedOperationException](ZeroUseIterable[Int]().reduceRight(_ + _), _.contains("reduceRight")) From c212347c87d1325e96b169baf86a138140250ca8 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 3 Oct 2022 12:28:45 -0700 Subject: [PATCH 094/261] Inherited members can induce ambiguity Spec binding precedence of inherited definition. Use -Ylegacy-binding for old precedence. --- spec/02-identifiers-names-and-scopes.md | 16 ++-- .../scala/reflect/quasiquotes/Parsers.scala | 2 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 4 +- .../tools/nsc/settings/ScalaSettings.scala | 2 + .../tools/nsc/typechecker/Contexts.scala | 75 +++++++++++++------ .../typechecker/MacroAnnotationNamers.scala | 6 +- .../scala/reflect/internal/Types.scala | 4 +- src/reflect/scala/reflect/io/ZipArchive.scala | 2 +- .../tools/nsc/doc/model/ModelFactory.scala | 2 +- test/files/neg/t11921.check | 8 ++ test/files/neg/t11921.scala | 16 ++++ test/files/neg/t11921b.check | 29 +++++++ test/files/neg/t11921b.scala | 66 ++++++++++++++++ test/files/neg/t11921c.check | 6 ++ test/files/neg/t11921c.scala | 17 +++++ test/files/neg/t1477.check | 2 +- test/files/neg/t1477.scala | 2 +- test/files/pos/t0165.scala | 2 +- test/files/pos/t11921a.scala | 18 +++++ test/files/pos/t11921b.scala | 67 +++++++++++++++++ test/files/pos/t11921c.scala | 23 ++++++ .../macroPlugins-macroExpand/Plugin_1.scala | 2 +- .../collection/mutable/HashSetTest.scala | 25 ++----- 23 files changed, 334 insertions(+), 62 deletions(-) create mode 100644 test/files/neg/t11921.check create mode 100644 test/files/neg/t11921.scala create mode 100644 test/files/neg/t11921b.check create mode 100644 test/files/neg/t11921b.scala create mode 100644 test/files/neg/t11921c.check create mode 100644 test/files/neg/t11921c.scala create mode 100644 test/files/pos/t11921a.scala create mode 100644 test/files/pos/t11921b.scala create mode 100644 test/files/pos/t11921c.scala diff --git a/spec/02-identifiers-names-and-scopes.md b/spec/02-identifiers-names-and-scopes.md index 855e6844af12..8ae89aec1178 100644 --- a/spec/02-identifiers-names-and-scopes.md +++ b/spec/02-identifiers-names-and-scopes.md @@ -7,7 +7,7 @@ chapter: 2 # Identifiers, Names and Scopes Names in Scala identify types, values, methods, and classes which are -collectively called _entities_. Names are introduced by local +collectively called _entities_. Names are introduced by [definitions and declarations](04-basic-declarations-and-definitions.html#basic-declarations-and-definitions), [inheritance](05-classes-and-objects.html#class-members), [import clauses](04-basic-declarations-and-definitions.html#import-clauses), or @@ -17,14 +17,16 @@ which are collectively called _bindings_. Bindings of each kind are assigned a precedence which determines whether one binding can shadow another: -1. Definitions and declarations that are local, inherited, or made - available by a package clause and also defined in the same compilation unit - as the reference to them, have the highest precedence. +1. Definitions and declarations in lexical scope that are not [top-level](09-top-level-definitions.html) + have the highest precedence. +1. Definitions and declarations that are either inherited, + or made available by a package clause and also defined in the same compilation unit as the reference to them, + have the next highest precedence. 1. Explicit imports have the next highest precedence. 1. Wildcard imports have the next highest precedence. -1. Bindings made available by a package clause, but not also defined in the - same compilation unit as the reference to them, as well as bindings - supplied by the compiler but not explicitly written in source code, +1. Bindings made available by a package clause, + but not also defined in the same compilation unit as the reference to them, + as well as bindings supplied by the compiler but not explicitly written in source code, have the lowest precedence. There are two different name spaces, one for [types](03-types.html#types) diff --git a/src/compiler/scala/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/reflect/quasiquotes/Parsers.scala index eb1310ed0d80..4db46c5f9d03 100644 --- a/src/compiler/scala/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Parsers.scala @@ -92,7 +92,7 @@ trait Parsers { self: Quasiquotes => case _ => gen.mkBlock(stats, doFlatten = true) } case nme.unapply => gen.mkBlock(stats, doFlatten = false) - case other => global.abort("unreachable") + case other => this.global.abort("unreachable") } // tq"$a => $b" diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f809182abdcb..460b5795ab72 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2098,7 +2098,7 @@ self => if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start, scala3Wildcard) else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) } } else { - typ() match { + this.typ() match { case Ident(name: TypeName) if nme.isVariableName(name) => atPos(start) { Bind(name, EmptyTree) } case t => t @@ -2289,7 +2289,7 @@ self => } /** The implementation of the context sensitive methods for parsing outside of patterns. */ final val outPattern = new PatternContextSensitive { - def argType(): Tree = typ() + def argType(): Tree = this.typ() def functionArgType(): Tree = paramType(repeatedParameterOK = false, useStartAsPosition = true) } /** The implementation for parsing inside of patterns at points where sequences are allowed. */ diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index e551cfec3c30..8f06c0022612 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -267,6 +267,8 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method") + val legacyBinding = BooleanSetting("-Ylegacy-binding", "Observe legacy name binding preference for inherited member competing with local definition.") + // Allows a specialised jar to be written. For instance one that provides stable hashing of content, or customisation of the file storage val YjarFactory = StringSetting ("-YjarFactory", "classname", "factory for jar files", classOf[DefaultJarFactory].getName) val YaddBackendThreads = IntSetting ("-Ybackend-parallelism", "maximum worker threads for backend", 1, Some((1,16)), (x: String) => None ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index ba06feb4c2e3..421565fd69b0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -51,13 +51,6 @@ trait Contexts { self: Analyzer => } private lazy val NoJavaMemberFound = (NoType, NoSymbol) - def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = - LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") - def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = - LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - def ambiguousDefinitions(owner: Symbol, other: Symbol) = - LookupAmbiguous(s"it is both defined in $owner and available as ${other.fullLocationString}") - private lazy val startContext = NoContext.make( Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, @@ -1362,6 +1355,15 @@ trait Contexts { self: Analyzer => private[this] var pre: Type = _ // the prefix type of defSym, if a class member private[this] var cx: Context = _ // the context under consideration private[this] var symbolDepth: Int = _ // the depth of the directly found symbol + private[this] var foundInPrefix: Boolean = _ // the symbol was found in pre + private[this] var foundInSuper: Boolean = _ // the symbol was found super of context class (inherited) + + def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) = + LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") + def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = + LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") + def ambiguousDefinitions(owner: Symbol, other: Symbol, addendum: String) = + LookupAmbiguous(s"it is both defined in $owner and available as ${other.fullLocationString}$addendum") def apply(thisContext: Context, name: Name)(qualifies: Symbol => Boolean): NameLookup = { lookupError = null @@ -1370,6 +1372,8 @@ trait Contexts { self: Analyzer => pre = NoPrefix cx = thisContext symbolDepth = -1 + foundInPrefix = false + foundInSuper = false def finish(qual: Tree, sym: Symbol): NameLookup = ( if (lookupError ne null) lookupError @@ -1386,8 +1390,8 @@ trait Contexts { self: Analyzer => finish(qual, sym) } - def lookupInPrefix(name: Name): Symbol = { - if (thisContext.unit.isJava) { + def lookupInPrefix(name: Name): Symbol = + if (thisContext.unit.isJava) thisContext.javaFindMember(pre, name, qualifies) match { case (_, NoSymbol) => NoSymbol @@ -1395,17 +1399,16 @@ trait Contexts { self: Analyzer => pre = pre1 sym } - } else { + else pre.member(name).filter(qualifies) - } - } + def accessibleInPrefix(s: Symbol) = thisContext.isAccessible(s, pre, superAccess = false) def searchPrefix = { cx = cx.enclClass val found0 = lookupInPrefix(name) - val found1 = found0 filter accessibleInPrefix + val found1 = found0.filter(accessibleInPrefix) if (found0.exists && !found1.exists && inaccessible == null) inaccessible = LookupInaccessible(found0, analyzer.lastAccessCheckDetails) @@ -1452,15 +1455,20 @@ trait Contexts { self: Analyzer => } // cx.scope eq null arises during FixInvalidSyms in Duplicators - def nextDefinition(): Unit = + def nextDefinition(): Unit = { + var inPrefix = false + defSym = NoSymbol while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { pre = cx.enclClass.prefix - defSym = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope) match { - case NoSymbol => searchPrefix - case found => found + defSym = lookupInScope(cx.owner, pre, cx.scope) match { + case NoSymbol => inPrefix = true; searchPrefix + case found => inPrefix = false; found } if (!defSym.exists) cx = cx.outer // push further outward } + foundInPrefix = inPrefix && defSym.exists + foundInSuper = foundInPrefix && defSym.owner != cx.owner + } nextDefinition() if (symbolDepth < 0) @@ -1490,8 +1498,11 @@ trait Contexts { self: Analyzer => * * Scala: Bindings of different kinds have a defined precedence order: * - * 1) Definitions and declarations that are local, inherited, or made available by - * a package clause and also defined in the same compilation unit as the reference, have highest precedence. + * 1) Local definitions and declarations have the highest precedence. + * 1b) Definitions and declarations that are either inherited, or made + * available by a package clause and also defined in the same compilation unit + * as the reference to them, have the next highest precedence. + * (Same precedence as 1 under -Ylegacy-binding.) * 2) Explicit imports have next highest precedence. * 3) Wildcard imports have next highest precedence. * 4) Definitions made available by a package clause, but not also defined in the same compilation unit @@ -1552,22 +1563,38 @@ trait Contexts { self: Analyzer => return ambiguousDefnAndImport(defSym.alternatives.head.owner, imp1) }) - // If the defSym is at 4, and there is a def at 1 in scope due to packaging, then the reference is ambiguous. - if (foreignDefined && !defSym.hasPackageFlag && !thisContext.unit.isJava) { + // If the defSym is at 4, and there is a def at 1b in scope due to packaging, then the reference is ambiguous. + // Also if defSym is at 1b inherited, the reference can be rendered ambiguous by a def at 1a in scope. + val possiblyAmbiguousDefinition = + foundInSuper && cx.owner.isClass && !settings.legacyBinding.value || + foreignDefined && !defSym.hasPackageFlag + if (possiblyAmbiguousDefinition && !thisContext.unit.isJava) { val defSym0 = defSym val pre0 = pre val cx0 = cx + val wasFoundInSuper = foundInSuper + val foundCompetingSymbol: () => Boolean = + if (foreignDefined) () => !foreignDefined + else () => !defSym.isTopLevel && !defSym.owner.isPackageObjectOrClass && !foundInSuper && !foreignDefined while ((cx ne NoContext) && cx.depth >= symbolDepth) cx = cx.outer + if (wasFoundInSuper) + while ((cx ne NoContext) && (cx.owner eq cx0.owner)) cx = cx.outer var done = false while (!done) { - defSym = NoSymbol nextDefinition() - done = (cx eq NoContext) || defSym.exists && !foreignDefined + done = (cx eq NoContext) || defSym.exists && foundCompetingSymbol() if (!done && (cx ne NoContext)) cx = cx.outer } if (defSym.exists && (defSym ne defSym0)) { + val addendum = + if (wasFoundInSuper) + s"""| + |Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. + |If shadowing was intended, write `this.${defSym0.name}`. + |Or use `-Ylegacy-binding` to enable the previous behavior everywhere.""".stripMargin + else "" val ambiguity = - if (preferDef) ambiguousDefinitions(owner = defSym.owner, defSym0) + if (preferDef) ambiguousDefinitions(owner = defSym.owner, defSym0, addendum) else ambiguousDefnAndImport(owner = defSym.owner, imp1) return ambiguity } diff --git a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala index de3a5facd6f3..1a4ab24e748c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala @@ -386,7 +386,7 @@ trait MacroAnnotationNamers { self: Analyzer => sym.setInfo(new MaybeExpandeeCompleter(tree) { override def kind = s"maybeExpandeeCompleter for ${sym.accurateKindString} ${sym.rawname}#${sym.id}" override def maybeExpand(): Unit = { - val companion = if (tree.isInstanceOf[ClassDef]) patchedCompanionSymbolOf(sym, context) else NoSymbol + val companion = if (this.tree.isInstanceOf[ClassDef]) patchedCompanionSymbolOf(sym, context) else NoSymbol def maybeExpand(annotation: Tree, annottee: Tree, maybeExpandee: Tree): Option[List[Tree]] = if (context.macrosEnabled) { // TODO: when is this bit flipped -- can we pull this check out farther? @@ -395,7 +395,7 @@ trait MacroAnnotationNamers { self: Analyzer => if (mann.isClass && mann.hasFlag(MACRO)) { assert(!currentRun.compiles(mann), mann) val annm = prepareAnnotationMacro(annotation, mann, sym, annottee, maybeExpandee) - expandAnnotationMacro(tree, annm) + expandAnnotationMacro(this.tree, annm) // if we encounter an error, we just return None, so that other macro annotations can proceed // this is unlike macroExpand1 when any error in an expandee blocks expansions // there it's necessary in order not to exacerbate typer errors @@ -424,7 +424,7 @@ trait MacroAnnotationNamers { self: Analyzer => enterSyms(expanded) // TODO: we can't reliably expand into imports, because they won't be accounted by definitions below us case None => markNotExpandable(sym) - finishSymbolNotExpandee(tree) + finishSymbolNotExpandee(this.tree) } // take care of the companion if it's no longer needed diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index f68ac6ddec46..9ab54deaa744 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -772,8 +772,8 @@ trait Types def withFilter(p: Type => Boolean) = new FilterMapForeach(p) class FilterMapForeach(p: Type => Boolean) extends FilterTypeCollector(p){ - def foreach[U](f: Type => U): Unit = collect(Type.this) foreach f - def map[T](f: Type => T): List[T] = collect(Type.this) map f + def foreach[U](f: Type => U): Unit = this.collect(Type.this).foreach(f) + def map[T](f: Type => T): List[T] = this.collect(Type.this).map(f) } @inline final def orElse(alt: => Type): Type = if (this ne NoType) this else alt diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 5f2c988b32c8..b8cdbbacc1b1 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -392,7 +392,7 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) { if (!zipEntry.isDirectory) { class FileEntry() extends Entry(zipEntry.getName) { override def lastModified = zipEntry.getTime() - override def input = resourceInputStream(path) + override def input = resourceInputStream(this.path) override def sizeOption = None } val f = new FileEntry() diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index b482885ba684..88c5b39c8dec 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -927,7 +927,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } else None def resultType = - makeTypeInTemplateContext(aSym.tpe, inTpl, aSym) + makeTypeInTemplateContext(aSym.tpe, this.inTpl, aSym) def isImplicit = aSym.isImplicit } diff --git a/test/files/neg/t11921.check b/test/files/neg/t11921.check new file mode 100644 index 000000000000..1f01253263fd --- /dev/null +++ b/test/files/neg/t11921.check @@ -0,0 +1,8 @@ +t11921.scala:6: error: reference to coll is ambiguous; +it is both defined in method lazyMap and available as method coll in trait Iterable +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.coll`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + def iterator = coll.iterator.map(f) // coll is ambiguous + ^ +1 error diff --git a/test/files/neg/t11921.scala b/test/files/neg/t11921.scala new file mode 100644 index 000000000000..01269d3895f1 --- /dev/null +++ b/test/files/neg/t11921.scala @@ -0,0 +1,16 @@ + + +class C { + def lazyMap[A, B](coll: Iterable[A], f: A => B) = + new Iterable[B] { + def iterator = coll.iterator.map(f) // coll is ambiguous + } +} + +/* was: +t11921.scala:5: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) + ^ +*/ diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check new file mode 100644 index 000000000000..42353ed2c3fd --- /dev/null +++ b/test/files/neg/t11921b.check @@ -0,0 +1,29 @@ +t11921b.scala:11: error: reference to x is ambiguous; +it is both defined in object Test and available as value x in class C +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.x`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + println(x) // error + ^ +t11921b.scala:15: error: reference to x is ambiguous; +it is both defined in object Test and available as value x in class C +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.x`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + println(x) // error + ^ +t11921b.scala:26: error: reference to y is ambiguous; +it is both defined in method c and available as value y in class D +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.y`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + println(y) // error + ^ +t11921b.scala:38: error: reference to y is ambiguous; +it is both defined in method c and available as value y in class D +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.y`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + println(y) // error + ^ +4 errors diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala new file mode 100644 index 000000000000..a01e47508d72 --- /dev/null +++ b/test/files/neg/t11921b.scala @@ -0,0 +1,66 @@ + + +object test1 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + println(x) // error + } + def f() = + new C { + println(x) // error + } + } +} + +object test2 { + def c(y: Float) = { + class D { + val y = 2 + } + new D { + println(y) // error + } + } +} + +object test3 { + def c(y: Float) = { + class D { + val y = 2 + } + class E extends D { + class F { + println(y) // error + } + } + } +} + +object test4 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + def x(y: Int) = 3 + val y: Int = this.x // OK + val z: Int = x // OK + } + } +} + +object global + +class C { + val global = 42 +} +object D extends C { + println(global) // OK, since global is defined in package +} diff --git a/test/files/neg/t11921c.check b/test/files/neg/t11921c.check new file mode 100644 index 000000000000..75aa9edd7d5c --- /dev/null +++ b/test/files/neg/t11921c.check @@ -0,0 +1,6 @@ +t11921c.scala:7: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) // coll is ambiguous + ^ +1 error diff --git a/test/files/neg/t11921c.scala b/test/files/neg/t11921c.scala new file mode 100644 index 000000000000..2f299f945392 --- /dev/null +++ b/test/files/neg/t11921c.scala @@ -0,0 +1,17 @@ + +// scalac: -Ylegacy-binding + +class C { + def lazyMap[A, B](coll: Iterable[A], f: A => B) = + new Iterable[B] { + def iterator = coll.iterator.map(f) // coll is ambiguous + } +} + +/* was: +t11921.scala:5: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) + ^ +*/ diff --git a/test/files/neg/t1477.check b/test/files/neg/t1477.check index 032a7bb747c8..4893654ecf85 100644 --- a/test/files/neg/t1477.check +++ b/test/files/neg/t1477.check @@ -1,5 +1,5 @@ t1477.scala:13: error: volatile type member cannot override type member with non-volatile upper bound: type V <: Middle.this.D (defined in trait C) - type V <: (D with U) + type V <: (this.D with U) ^ 1 error diff --git a/test/files/neg/t1477.scala b/test/files/neg/t1477.scala index a9a6d678ca5f..9e4bcba224b7 100644 --- a/test/files/neg/t1477.scala +++ b/test/files/neg/t1477.scala @@ -10,7 +10,7 @@ object Test extends App { } trait Middle extends C { - type V <: (D with U) + type V <: (this.D with U) } class D extends Middle { diff --git a/test/files/pos/t0165.scala b/test/files/pos/t0165.scala index 76aef8524017..ca47eb655102 100644 --- a/test/files/pos/t0165.scala +++ b/test/files/pos/t0165.scala @@ -4,7 +4,7 @@ import scala.collection.mutable.LinkedHashMap trait Main { def asMany : ArrayResult = { object result extends LinkedHashMap[String,String] with ArrayResult { - def current = result + def current = this.result } result } diff --git a/test/files/pos/t11921a.scala b/test/files/pos/t11921a.scala new file mode 100644 index 000000000000..5bee630e57c1 --- /dev/null +++ b/test/files/pos/t11921a.scala @@ -0,0 +1,18 @@ + +class C(x: Int) { + class D extends C(42) { + def f() = println(x) + } +} + +trait T { + val t: Int +} +trait U extends T { + val t: Int + import t._ +} +trait V { this: T => + val t: Int + import t._ +} diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala new file mode 100644 index 000000000000..a8e4bb783250 --- /dev/null +++ b/test/files/pos/t11921b.scala @@ -0,0 +1,67 @@ + +// scalac: -Werror -Ylegacy-binding + +object test1 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + println(x) // error + } + def f() = + new C { + println(x) // error + } + } +} + +object test2 { + def c(y: Float) = { + class D { + val y = 2 + } + new D { + println(y) // error + } + } +} + +object test3 { + def c(y: Float) = { + class D { + val y = 2 + } + class E extends D { + class F { + println(y) // error + } + } + } +} + +object test4 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + def x(y: Int) = 3 + val y: Int = this.x // OK + val z: Int = x // OK + } + } +} + +object global + +class C { + val global = 42 +} +object D extends C { + println(global) // OK, since global is defined in package +} diff --git a/test/files/pos/t11921c.scala b/test/files/pos/t11921c.scala new file mode 100644 index 000000000000..d73c4c31536e --- /dev/null +++ b/test/files/pos/t11921c.scala @@ -0,0 +1,23 @@ + +// test/scaladoc/resources/t5784.scala + +package test.templates { + object `package` { + type String = java.lang.String + val String = new StringCompanion + class StringCompanion { def boo = ??? } + } + + trait Base { + type String = test.templates.String + type T <: Foo + val T: FooExtractor + trait Foo { def foo: Int } + trait FooExtractor { def apply(foo: Int): Unit; def unapply(t: Foo): Option[Int] } + } + + trait Api extends Base { + override type T <: FooApi + trait FooApi extends Foo { def bar: String } + } +} diff --git a/test/files/run/macroPlugins-macroExpand/Plugin_1.scala b/test/files/run/macroPlugins-macroExpand/Plugin_1.scala index f62c34a1b102..bbccd32ededf 100644 --- a/test/files/run/macroPlugins-macroExpand/Plugin_1.scala +++ b/test/files/run/macroPlugins-macroExpand/Plugin_1.scala @@ -18,7 +18,7 @@ class Plugin(val global: Global) extends NscPlugin { object expander extends DefMacroExpander(typer, expandee, mode, pt) { override def onSuccess(expanded: Tree) = { val message = s"expanded into ${expanded.toString}" - typer.typed(q"println($message)") + this.typer.typed(q"println($message)") } } Some(expander(expandee)) diff --git a/test/junit/scala/collection/mutable/HashSetTest.scala b/test/junit/scala/collection/mutable/HashSetTest.scala index 502b8faf3527..36ed3400f1a9 100644 --- a/test/junit/scala/collection/mutable/HashSetTest.scala +++ b/test/junit/scala/collection/mutable/HashSetTest.scala @@ -86,28 +86,19 @@ class HashSetTest { } class OnceOnly extends IterableOnce[Int] { - override def knownSize: Int = 1 - - var iterated:Boolean = false - - override def iterator: Iterator[Int] = { - new Iterator[Int] { - assert(!iterated) - iterated = true - private var v = Option(42) - override def hasNext: Boolean = v.nonEmpty && knownSize > 0 - override def next(): Int = { - val res = v.get - v = None - res - } - } + var iterated = false + + override def iterator = { + assertFalse("Attempt to re-iterate!", iterated) + iterated = true + Iterator.single(42) } } @Test - def addAllTest2(): Unit = { + def `addAll adds exactly once`: Unit = { val hs = HashSet.empty[Int] hs.addAll(new OnceOnly) + assertEquals(1, hs.size) } } From fb495924c87384441c2de652ea5f653eb0bb3df8 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 15 Nov 2022 11:37:25 +0100 Subject: [PATCH 095/261] don't report type / term aliases as ambiguous --- .../tools/nsc/typechecker/Contexts.scala | 10 +++-- test/files/neg/t11921-alias.check | 15 +++++++ test/files/neg/t11921-alias.scala | 39 +++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/t11921-alias.check create mode 100644 test/files/neg/t11921-alias.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 421565fd69b0..c17188229313 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1455,7 +1455,7 @@ trait Contexts { self: Analyzer => } // cx.scope eq null arises during FixInvalidSyms in Duplicators - def nextDefinition(): Unit = { + def nextDefinition(lastDef: Symbol, lastPre: Type): Unit = { var inPrefix = false defSym = NoSymbol while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { @@ -1466,10 +1466,14 @@ trait Contexts { self: Analyzer => } if (!defSym.exists) cx = cx.outer // push further outward } + if ((defSym.isAliasType || lastDef.isAliasType) && pre.memberType(defSym) =:= lastPre.memberType(lastDef)) + defSym = NoSymbol + if (defSym.isStable && lastDef.isStable && singleType(pre, defSym) =:= singleType(lastPre, lastDef)) + defSym = NoSymbol foundInPrefix = inPrefix && defSym.exists foundInSuper = foundInPrefix && defSym.owner != cx.owner } - nextDefinition() + nextDefinition(NoSymbol, NoPrefix) if (symbolDepth < 0) symbolDepth = cx.depth @@ -1581,7 +1585,7 @@ trait Contexts { self: Analyzer => while ((cx ne NoContext) && (cx.owner eq cx0.owner)) cx = cx.outer var done = false while (!done) { - nextDefinition() + nextDefinition(defSym0, pre0) done = (cx eq NoContext) || defSym.exists && foundCompetingSymbol() if (!done && (cx ne NoContext)) cx = cx.outer } diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check new file mode 100644 index 000000000000..847486c35489 --- /dev/null +++ b/test/files/neg/t11921-alias.check @@ -0,0 +1,15 @@ +t11921-alias.scala:16: error: reference to TT is ambiguous; +it is both defined in object O and available as type TT in class C +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.TT`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + def n(x: TT) = x // ambiguous + ^ +t11921-alias.scala:36: error: reference to c is ambiguous; +it is both defined in class B and available as value c in class A +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.c`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + def n = c // ambiguous + ^ +2 errors diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala new file mode 100644 index 000000000000..5cbbaac6168c --- /dev/null +++ b/test/files/neg/t11921-alias.scala @@ -0,0 +1,39 @@ +object t1 { + class C[T] { type TT = T } + object O { + type TT = String + class D extends C[TT] { + def n(x: TT) = x // OK + } + } +} + +object t2 { + class C[T] { type TT <: T } + object O { + type TT = String + class D extends C[TT] { + def n(x: TT) = x // ambiguous + } + } +} + +object t3 { + trait Context + class A[C <: Context](val c: C) + class B(val c: Context) { b => + val a = new A[c.type](c) { + def n = c // OK + } + } +} + +object t4 { + trait Context + class A[C <: Context](val c: C) + class B(val c: Context) { b => + val a = new A(c) { + def n = c // ambiguous + } + } +} From 70130daaa0592a86430f47004a9e25a852085a5b Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 15 Nov 2022 13:25:45 +0100 Subject: [PATCH 096/261] unthis --- src/compiler/scala/reflect/quasiquotes/Parsers.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/reflect/quasiquotes/Parsers.scala index 4db46c5f9d03..eb1310ed0d80 100644 --- a/src/compiler/scala/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Parsers.scala @@ -92,7 +92,7 @@ trait Parsers { self: Quasiquotes => case _ => gen.mkBlock(stats, doFlatten = true) } case nme.unapply => gen.mkBlock(stats, doFlatten = false) - case other => this.global.abort("unreachable") + case other => global.abort("unreachable") } // tq"$a => $b" From 12c758ebd54f174ede162ae118350ab45b45d44b Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 6 Dec 2022 13:09:09 +0100 Subject: [PATCH 097/261] change check for singletons --- .../scala/tools/nsc/typechecker/Contexts.scala | 3 ++- test/files/neg/t11921-alias.scala | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index c17188229313..e0b629feeddc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1468,7 +1468,8 @@ trait Contexts { self: Analyzer => } if ((defSym.isAliasType || lastDef.isAliasType) && pre.memberType(defSym) =:= lastPre.memberType(lastDef)) defSym = NoSymbol - if (defSym.isStable && lastDef.isStable && singleType(pre, defSym) =:= singleType(lastPre, lastDef)) + if (defSym.isStable && lastDef.isStable && + (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef)) defSym = NoSymbol foundInPrefix = inPrefix && defSym.exists foundInSuper = foundInPrefix && defSym.owner != cx.owner diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala index 5cbbaac6168c..8e05ecf1e5aa 100644 --- a/test/files/neg/t11921-alias.scala +++ b/test/files/neg/t11921-alias.scala @@ -37,3 +37,13 @@ object t4 { } } } + +object t5 { + trait TT + class K[T <: TT](val t: T) + class C { + def f(t: TT) = new K[t.type](t) { + def test = t + } + } +} From 225f39bab56c92a0e18766191ca5d0f0cf5704d7 Mon Sep 17 00:00:00 2001 From: Ruslans Tarasovs Date: Wed, 7 Dec 2022 13:06:36 +0200 Subject: [PATCH 098/261] Do not embed absolute path into class files --- src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala index 8f59b22ccf38..17f2027dbb37 100644 --- a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala +++ b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala @@ -357,7 +357,7 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL { else rhs private def mkCheckedAccessorRhs(retVal: Tree, pos: Position, bitmap: BitmapInfo): Tree = { - val msg = s"Uninitialized field: ${clazz.sourceFile}: ${pos.line}" + val msg = s"Uninitialized field: ${clazz.sourceFile.name}: ${pos.line}" val result = IF(mkTest(bitmap, equalToZero = false)). THEN(retVal). From 189fc859e9639bbef1a93bca206d3537953ff20f Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 12 Dec 2022 15:09:49 +0100 Subject: [PATCH 099/261] heuristic for forwarded class parameters --- .../tools/nsc/typechecker/Contexts.scala | 19 ++++++++++++++++++- test/files/neg/t11921-alias.check | 9 ++++++++- test/files/neg/t11921-alias.scala | 19 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index e0b629feeddc..8958c014fb92 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1466,10 +1466,27 @@ trait Contexts { self: Analyzer => } if (!defSym.exists) cx = cx.outer // push further outward } + def forwardedClassParam = lastDef.isParamAccessor && { + val parentClass = lastDef.owner + val templateCtx = thisContext.nextEnclosing(c => c.tree.isInstanceOf[Template] && c.owner.isNonBottomSubClass(parentClass)) + templateCtx.owner.superClass == parentClass && { + templateCtx.tree.asInstanceOf[Template].parents.headOption.collect { + case Apply(_, args) => args + } match { + case Some(args) => + // named args? repeated args? + args.zip(parentClass.primaryConstructor.paramss.headOption.getOrElse(Nil)).exists { + case (arg: Ident, param) => param.name == lastDef.name && arg.symbol == defSym + case _ => false + } + case _ => false + } + } + } if ((defSym.isAliasType || lastDef.isAliasType) && pre.memberType(defSym) =:= lastPre.memberType(lastDef)) defSym = NoSymbol if (defSym.isStable && lastDef.isStable && - (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef)) + (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef || forwardedClassParam)) defSym = NoSymbol foundInPrefix = inPrefix && defSym.exists foundInSuper = foundInPrefix && defSym.owner != cx.owner diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check index 847486c35489..cdfe180b6a29 100644 --- a/test/files/neg/t11921-alias.check +++ b/test/files/neg/t11921-alias.check @@ -12,4 +12,11 @@ If shadowing was intended, write `this.c`. Or use `-Ylegacy-binding` to enable the previous behavior everywhere. def n = c // ambiguous ^ -2 errors +t11921-alias.scala:65: error: reference to name is ambiguous; +it is both defined in method m and available as value name in class A +Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +If shadowing was intended, write `this.name`. +Or use `-Ylegacy-binding` to enable the previous behavior everywhere. + println(name) + ^ +3 errors diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala index 8e05ecf1e5aa..8505012af38d 100644 --- a/test/files/neg/t11921-alias.scala +++ b/test/files/neg/t11921-alias.scala @@ -47,3 +47,22 @@ object t5 { } } } + +object t6 { + class C(val name: String) + object Test { + def m(name: String) = new C(name) { + println(name) + } + } +} + +object t7 { + abstract class A(val name: String) + class C(name: String) extends A(name) + object Test { + def m(name: String) = new C(name) { + println(name) + } + } +} From 9098cc7ab4ca3c3a372e0733d246f3f863439f22 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 13 Dec 2022 14:23:04 +0100 Subject: [PATCH 100/261] Fix incorrect cast in StrictOptimizedMapOps.+ --- src/library/scala/collection/StrictOptimizedMapOps.scala | 8 ++++++-- test/junit/scala/collection/immutable/MapTest.scala | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/library/scala/collection/StrictOptimizedMapOps.scala b/src/library/scala/collection/StrictOptimizedMapOps.scala index 02cf26b490f6..1f5791bbb718 100644 --- a/src/library/scala/collection/StrictOptimizedMapOps.scala +++ b/src/library/scala/collection/StrictOptimizedMapOps.scala @@ -38,7 +38,11 @@ trait StrictOptimizedMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C @deprecated("Use ++ with an explicit collection argument instead of + with varargs", "2.13.0") override def + [V1 >: V](elem1: (K, V1), elem2: (K, V1), elems: (K, V1)*): CC[K, V1] = { - val m = ((this + elem1).asInstanceOf[Map[K, V]] + elem2).asInstanceOf[CC[K, V1]] - if(elems.isEmpty) m else m.concat(elems).asInstanceOf[CC[K, V1]] + val b = mapFactory.newBuilder[K, V1] + b ++= this + b += elem1 + b += elem2 + if (elems.nonEmpty) b ++= elems + b.result() } } diff --git a/test/junit/scala/collection/immutable/MapTest.scala b/test/junit/scala/collection/immutable/MapTest.scala index 6846a3bcf0c2..28b6fba9fba5 100644 --- a/test/junit/scala/collection/immutable/MapTest.scala +++ b/test/junit/scala/collection/immutable/MapTest.scala @@ -3,6 +3,8 @@ package scala.collection.immutable import org.junit.Assert.assertEquals import org.junit.Test +import scala.annotation.nowarn + class MapTest { @Test def builderCompare1(): Unit = { @@ -141,4 +143,11 @@ class MapTest { } } } + + @Test @nowarn("cat=deprecation") + def t12699(): Unit = { + val m1: HashMap[Int, Int] = HashMap(1 -> 1) + assertEquals(7, m1.+(elem1 = 2 -> 2, elem2 = 3 -> 3, elems = List( 4 -> 4, 5 -> 5, 6 -> 6, 7 -> 7): _*).size) + assertEquals(7, m1.+(2 -> 2, 3 -> 3, 4 -> 4, 5 -> 5, 6 -> 6, 7 -> 7).size) + } } From 9f0133f40e08d397f6fe47d1bfe8c1a487f888bf Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 13 Dec 2022 22:34:17 +0100 Subject: [PATCH 101/261] remove heuristic, turn into a warning --- .../tools/nsc/settings/ScalaSettings.scala | 2 - .../tools/nsc/typechecker/Contexts.scala | 60 +++++++++---------- .../scala/tools/nsc/typechecker/Typers.scala | 2 + .../reflect/internal/StdAttachments.scala | 2 + .../reflect/runtime/JavaUniverseForce.scala | 1 + test/files/neg/t11921-alias.check | 41 ++++++++----- test/files/neg/t11921-alias.scala | 2 + test/files/neg/t11921.check | 16 +++-- test/files/neg/t11921b.check | 51 +++++++++------- test/files/neg/t11921b.scala | 15 ++++- test/files/neg/t11921c.check | 2 +- test/files/neg/t11921c.scala | 3 +- test/files/pos/t11921b.scala | 2 +- 13 files changed, 118 insertions(+), 81 deletions(-) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 8f06c0022612..e551cfec3c30 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -267,8 +267,6 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method") - val legacyBinding = BooleanSetting("-Ylegacy-binding", "Observe legacy name binding preference for inherited member competing with local definition.") - // Allows a specialised jar to be written. For instance one that provides stable hashing of content, or customisation of the file storage val YjarFactory = StringSetting ("-YjarFactory", "classname", "factory for jar files", classOf[DefaultJarFactory].getName) val YaddBackendThreads = IntSetting ("-Ybackend-parallelism", "maximum worker threads for backend", 1, Some((1,16)), (x: String) => None ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 8958c014fb92..c06158828993 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1362,8 +1362,28 @@ trait Contexts { self: Analyzer => LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - def ambiguousDefinitions(owner: Symbol, other: Symbol, addendum: String) = - LookupAmbiguous(s"it is both defined in $owner and available as ${other.fullLocationString}$addendum") + def ambiguousDefinitions(sym: Symbol, other: Symbol, otherFoundInSuper: Boolean, otherEnclClass: Symbol) = { + if (otherFoundInSuper) { + val enclDesc = if (otherEnclClass.isAnonymousClass) "anonymous class" else otherEnclClass.toString + val parent = otherEnclClass.parentSymbols.find(_.isNonBottomSubClass(other.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != other.owner) s", inherited through parent $parent" else "" + val message = + s"""it is both defined in the enclosing ${sym.owner} and available in the enclosing $enclDesc as $other (defined in ${other.ownsString}$inherit) + |Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. + |To continue using the symbol from the superclass, write `this.${sym.name}`.""".stripMargin + if (currentRun.isScala3) + Some(LookupAmbiguous(message)) + else { + // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning + other.updateAttachment(LookupAmbiguityWarning( + s"""reference to ${sym.name} is ambiguous; + |$message + |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) + None + } + } else + Some(LookupAmbiguous(s"it is both defined in ${sym.owner} and available as ${other.fullLocationString}")) + } def apply(thisContext: Context, name: Name)(qualifies: Symbol => Boolean): NameLookup = { lookupError = null @@ -1466,27 +1486,10 @@ trait Contexts { self: Analyzer => } if (!defSym.exists) cx = cx.outer // push further outward } - def forwardedClassParam = lastDef.isParamAccessor && { - val parentClass = lastDef.owner - val templateCtx = thisContext.nextEnclosing(c => c.tree.isInstanceOf[Template] && c.owner.isNonBottomSubClass(parentClass)) - templateCtx.owner.superClass == parentClass && { - templateCtx.tree.asInstanceOf[Template].parents.headOption.collect { - case Apply(_, args) => args - } match { - case Some(args) => - // named args? repeated args? - args.zip(parentClass.primaryConstructor.paramss.headOption.getOrElse(Nil)).exists { - case (arg: Ident, param) => param.name == lastDef.name && arg.symbol == defSym - case _ => false - } - case _ => false - } - } - } if ((defSym.isAliasType || lastDef.isAliasType) && pre.memberType(defSym) =:= lastPre.memberType(lastDef)) defSym = NoSymbol if (defSym.isStable && lastDef.isStable && - (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef || forwardedClassParam)) + (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef)) defSym = NoSymbol foundInPrefix = inPrefix && defSym.exists foundInSuper = foundInPrefix && defSym.owner != cx.owner @@ -1524,7 +1527,7 @@ trait Contexts { self: Analyzer => * 1b) Definitions and declarations that are either inherited, or made * available by a package clause and also defined in the same compilation unit * as the reference to them, have the next highest precedence. - * (Same precedence as 1 under -Ylegacy-binding.) + * (Only in -Xsource:3, same precedence as 1 with a warning in Scala 2.) * 2) Explicit imports have next highest precedence. * 3) Wildcard imports have next highest precedence. * 4) Definitions made available by a package clause, but not also defined in the same compilation unit @@ -1588,7 +1591,7 @@ trait Contexts { self: Analyzer => // If the defSym is at 4, and there is a def at 1b in scope due to packaging, then the reference is ambiguous. // Also if defSym is at 1b inherited, the reference can be rendered ambiguous by a def at 1a in scope. val possiblyAmbiguousDefinition = - foundInSuper && cx.owner.isClass && !settings.legacyBinding.value || + foundInSuper && cx.owner.isClass || foreignDefined && !defSym.hasPackageFlag if (possiblyAmbiguousDefinition && !thisContext.unit.isJava) { val defSym0 = defSym @@ -1608,17 +1611,10 @@ trait Contexts { self: Analyzer => if (!done && (cx ne NoContext)) cx = cx.outer } if (defSym.exists && (defSym ne defSym0)) { - val addendum = - if (wasFoundInSuper) - s"""| - |Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |If shadowing was intended, write `this.${defSym0.name}`. - |Or use `-Ylegacy-binding` to enable the previous behavior everywhere.""".stripMargin - else "" val ambiguity = - if (preferDef) ambiguousDefinitions(owner = defSym.owner, defSym0, addendum) - else ambiguousDefnAndImport(owner = defSym.owner, imp1) - return ambiguity + if (preferDef) ambiguousDefinitions(defSym, defSym0, wasFoundInSuper, cx0.enclClass.owner) + else Some(ambiguousDefnAndImport(owner = defSym.owner, imp1)) + if (ambiguity.nonEmpty) return ambiguity.get } defSym = defSym0 pre = pre0 diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3e1ad14f80ae..ffe8dcb68d26 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5501,6 +5501,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case sym => typed1(tree setSymbol sym, mode, pt) } case LookupSucceeded(qual, sym) => + sym.getAndRemoveAttachment[LookupAmbiguityWarning].foreach(w => + runReporting.warning(tree.pos, w.msg, WarningCategory.Other, context.owner)) (// this -> Foo.this if (sym.isThisSym) typed1(This(sym.owner) setPos tree.pos, mode, pt) diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 2aefc327cca2..1f80be5f61cb 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -152,4 +152,6 @@ trait StdAttachments { /** For `val i = 42`, marks field as inferred so accessor (getter) can warn if implicit. */ case object FieldTypeInferred extends PlainAttachment + + case class LookupAmbiguityWarning(msg: String) extends PlainAttachment } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index b4d932c591d9..bebba5e10782 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -79,6 +79,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.RootSelection this.TypedExpectingUnitAttachment this.FieldTypeInferred + this.LookupAmbiguityWarning this.noPrint this.typeDebug // inaccessible: this.posAssigner diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check index cdfe180b6a29..db052d9ab7f0 100644 --- a/test/files/neg/t11921-alias.check +++ b/test/files/neg/t11921-alias.check @@ -1,22 +1,31 @@ -t11921-alias.scala:16: error: reference to TT is ambiguous; -it is both defined in object O and available as type TT in class C -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.TT`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921-alias.scala:18: warning: reference to TT is ambiguous; +it is both defined in the enclosing object O and available in the enclosing class D as type TT (defined in class C) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.TT`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n(x: TT) = x // ambiguous ^ -t11921-alias.scala:36: error: reference to c is ambiguous; -it is both defined in class B and available as value c in class A -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.c`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921-alias.scala:38: warning: reference to c is ambiguous; +it is both defined in the enclosing class B and available in the enclosing anonymous class as value c (defined in class A) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.c`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n = c // ambiguous ^ -t11921-alias.scala:65: error: reference to name is ambiguous; -it is both defined in method m and available as value name in class A -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.name`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921-alias.scala:57: warning: reference to name is ambiguous; +it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class C) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.name`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) ^ -3 errors +t11921-alias.scala:67: warning: reference to name is ambiguous; +it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.name`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(name) + ^ +error: No warnings can be incurred under -Werror. +4 warnings +1 error diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala index 8505012af38d..f238796da174 100644 --- a/test/files/neg/t11921-alias.scala +++ b/test/files/neg/t11921-alias.scala @@ -1,3 +1,5 @@ +// scalac: -Werror + object t1 { class C[T] { type TT = T } object O { diff --git a/test/files/neg/t11921.check b/test/files/neg/t11921.check index 1f01253263fd..9a63050f0ffb 100644 --- a/test/files/neg/t11921.check +++ b/test/files/neg/t11921.check @@ -1,8 +1,14 @@ -t11921.scala:6: error: reference to coll is ambiguous; -it is both defined in method lazyMap and available as method coll in trait Iterable -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.coll`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921.scala:6: error: type mismatch; + found : A => B + required: B => B + def iterator = coll.iterator.map(f) // coll is ambiguous + ^ +t11921.scala:6: warning: reference to coll is ambiguous; +it is both defined in the enclosing method lazyMap and available in the enclosing anonymous class as method coll (defined in trait Iterable) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.coll`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def iterator = coll.iterator.map(f) // coll is ambiguous ^ +1 warning 1 error diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 42353ed2c3fd..5311512794dd 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -1,29 +1,38 @@ -t11921b.scala:11: error: reference to x is ambiguous; -it is both defined in object Test and available as value x in class C -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.x`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921b.scala:11: warning: reference to x is ambiguous; +it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ -t11921b.scala:15: error: reference to x is ambiguous; -it is both defined in object Test and available as value x in class C -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.x`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921b.scala:15: warning: reference to x is ambiguous; +it is both defined in the enclosing object Test and available in the enclosing anonymous class as value x (defined in class C) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ -t11921b.scala:26: error: reference to y is ambiguous; -it is both defined in method c and available as value y in class D -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.y`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921b.scala:26: warning: reference to y is ambiguous; +it is both defined in the enclosing method c and available in the enclosing anonymous class as value y (defined in class D) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.y`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ -t11921b.scala:38: error: reference to y is ambiguous; -it is both defined in method c and available as value y in class D -Since 2.13.11, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -If shadowing was intended, write `this.y`. -Or use `-Ylegacy-binding` to enable the previous behavior everywhere. +t11921b.scala:38: warning: reference to y is ambiguous; +it is both defined in the enclosing method c and available in the enclosing class E as value y (defined in class D) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.y`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ -4 errors +t11921b.scala:75: warning: reference to x is ambiguous; +it is both defined in the enclosing object Uhu and available in the enclosing class C as value x (defined in class A, inherited through parent class B) +Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def t = x // ambiguous, message mentions parent B + ^ +error: No warnings can be incurred under -Werror. +5 warnings +1 error diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index a01e47508d72..433d17a8c5c2 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -1,4 +1,4 @@ - +// scalac: -Werror object test1 { @@ -64,3 +64,16 @@ class C { object D extends C { println(global) // OK, since global is defined in package } + +object test5 { + class A { val x = 1 } + class B extends A + object Uhu { + val x = 2 + class C extends B { + class Inner { + def t = x // ambiguous, message mentions parent B + } + } + } +} diff --git a/test/files/neg/t11921c.check b/test/files/neg/t11921c.check index 75aa9edd7d5c..1c2d1ba8adda 100644 --- a/test/files/neg/t11921c.check +++ b/test/files/neg/t11921c.check @@ -1,4 +1,4 @@ -t11921c.scala:7: error: type mismatch; +t11921c.scala:6: error: type mismatch; found : A => B required: B => B def iterator = coll.iterator.map(f) // coll is ambiguous diff --git a/test/files/neg/t11921c.scala b/test/files/neg/t11921c.scala index 2f299f945392..f528773d7554 100644 --- a/test/files/neg/t11921c.scala +++ b/test/files/neg/t11921c.scala @@ -1,5 +1,4 @@ - -// scalac: -Ylegacy-binding +// scalac: -Wconf:msg=legacy-binding:s class C { def lazyMap[A, B](coll: Iterable[A], f: A => B) = diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala index a8e4bb783250..ba9a370cb89a 100644 --- a/test/files/pos/t11921b.scala +++ b/test/files/pos/t11921b.scala @@ -1,5 +1,5 @@ -// scalac: -Werror -Ylegacy-binding +// scalac: -Werror -Wconf:msg=legacy-binding:s object test1 { From db51e0a418f8812550f0051bbaea39b716204b99 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 14 Dec 2022 12:11:59 +0100 Subject: [PATCH 102/261] latest diff from PR 10177 --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index c06158828993..6bcd81deb4df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1523,15 +1523,17 @@ trait Contexts { self: Analyzer => * * Scala: Bindings of different kinds have a defined precedence order: * - * 1) Local definitions and declarations have the highest precedence. + * 1) Definitions and declarations in lexical scope that are not top-level have the highest precedence. * 1b) Definitions and declarations that are either inherited, or made * available by a package clause and also defined in the same compilation unit * as the reference to them, have the next highest precedence. * (Only in -Xsource:3, same precedence as 1 with a warning in Scala 2.) * 2) Explicit imports have next highest precedence. * 3) Wildcard imports have next highest precedence. - * 4) Definitions made available by a package clause, but not also defined in the same compilation unit - * as the reference, have lowest precedence. Also "root" imports added implicitly. + * 4) Bindings made available by a package clause, + * but not also defined in the same compilation unit as the reference to them, + * as well as bindings supplied by the compiler but not explicitly written in source code, + * have the lowest precedence. */ def foreignDefined = defSym.exists && thisContext.isPackageOwnedInDifferentUnit(defSym) // SI-2458 From 3be2dce7a0580a015110fc660119cbd628ba4b1b Mon Sep 17 00:00:00 2001 From: Liang Yan Date: Sat, 3 Dec 2022 20:21:41 +0800 Subject: [PATCH 103/261] add getOrElse,getOrElseUpdate and other operation Add getOrElse, getOrElseUpdate, hashCode,sizeHint operation in LinkedHashMap. Add entry iterator in LinkedHashMap/LinkedHashSet. Add optimization in immutable.{HashMap, HashSet} and mutable.{HashMap, HashSet}. Signed-off-by: Liang Yan --- .../scala/collection/immutable/HashMap.scala | 47 +++++ .../scala/collection/immutable/HashSet.scala | 21 +++ .../scala/collection/mutable/HashMap.scala | 15 ++ .../scala/collection/mutable/HashSet.scala | 23 ++- .../collection/mutable/LinkedHashMap.scala | 163 +++++++++++++----- .../collection/mutable/LinkedHashSet.scala | 41 +++++ 6 files changed, 267 insertions(+), 43 deletions(-) diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 7a9231231d32..2e8378c4d810 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -190,6 +190,27 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: } } this + case lhm: mutable.LinkedHashMap[K @unchecked, V @unchecked] => + val iter = lhm.entryIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, next.value, originalHash, improved, 0, replaceValue = true) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, next.value, originalHash, improve(originalHash), 0, shallowlyMutableNodeMap) + } + return new HashMap(current) + } + } + this case _ => class accum extends AbstractFunction2[K, V1, Unit] with Function1[(K, V1), Unit] { var changed = false @@ -397,6 +418,24 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: } newHashMapOrThis(curr) } + case lhashSet: collection.mutable.LinkedHashSet[K] => + if (lhashSet.isEmpty) { + this + } else { + val iter = lhashSet.entryIterator + var curr = rootNode + + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhashSet.unimproveHash(next.hash) + val improved = improve(originalHash) + curr = curr.removed(next.key, originalHash, improved, 0) + if (curr.size == 0) { + return HashMap.empty + } + } + newHashMapOrThis(curr) + } case _ => val iter = keys.iterator var curr = rootNode @@ -2353,6 +2392,14 @@ private[immutable] final class HashMapBuilder[K, V] extends ReusableBuilder[(K, val hash = improve(originalHash) update(rootNode, next.key, next.value, originalHash, hash, 0) } + case lhm: collection.mutable.LinkedHashMap[K, V] => + val iter = lhm.entryIterator + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhm.unimproveHash(next.hash) + val hash = improve(originalHash) + update(rootNode, next.key, next.value, originalHash, hash, 0) + } case thatMap: Map[K, V] => thatMap.foreachEntry((key, value) => addOne(key, value)) case other => diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 1785ceb2c0ea..459fcf1682aa 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -121,6 +121,27 @@ final class HashSet[A] private[immutable](private[immutable] val rootNode: Bitma } } this + case lhs: collection.mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + var current = rootNode + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhs.unimproveHash(next.hash) + val improved = improve(originalHash) + current = current.updated(next.key, originalHash, improved, 0) + + if (current ne rootNode) { + var shallowlyMutableNodeMap = Node.bitposFrom(Node.maskFrom(improved, 0)) + while (iter.hasNext) { + val next = iter.next() + val originalHash = lhs.unimproveHash(next.hash) + val improved = improve(originalHash) + shallowlyMutableNodeMap = current.updateWithShallowMutations(next.key, originalHash, improved, 0, shallowlyMutableNodeMap) + } + return new HashSet(current) + } + } + this case _ => val iter = that.iterator var current = rootNode diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala index 3b0eaa817eb5..94f731e17967 100644 --- a/src/library/scala/collection/mutable/HashMap.scala +++ b/src/library/scala/collection/mutable/HashMap.scala @@ -108,6 +108,13 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) put0(next.key, next.value, next.hash, getOld = false) } this + case lhm: mutable.LinkedHashMap[K, V] => + val iter = lhm.entryIterator + while (iter.hasNext) { + val entry = iter.next() + put0(entry.key, entry.value,entry.hash,getOld = false) + } + this case thatMap: Map[K, V] => thatMap.foreachEntry { (key: K, value: V) => put0(key, value, improveHash(key.##), getOld = false) @@ -195,6 +202,14 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) if (size == 0) return this } this + case lhs: mutable.LinkedHashSet[K] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + remove0(next.key, next.hash) + if (size == 0) return this + } + this case _ => super.subtractAll(xs) } } diff --git a/src/library/scala/collection/mutable/HashSet.scala b/src/library/scala/collection/mutable/HashSet.scala index bf9e93575bf8..49be7fa4e9e2 100644 --- a/src/library/scala/collection/mutable/HashSet.scala +++ b/src/library/scala/collection/mutable/HashSet.scala @@ -93,11 +93,18 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) override def addAll(xs: IterableOnce[A]): this.type = { sizeHint(xs.knownSize) xs match { - case hm: immutable.HashSet[A] => - hm.foreachWithHash((k, h) => addElem(k, improveHash(h))) + case hs: immutable.HashSet[A] => + hs.foreachWithHash((k, h) => addElem(k, improveHash(h))) + this + case hs: mutable.HashSet[A] => + val iter = hs.nodeIterator + while (iter.hasNext) { + val next = iter.next() + addElem(next.key, next.hash) + } this - case hm: mutable.HashSet[A] => - val iter = hm.nodeIterator + case lhs: mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator while (iter.hasNext) { val next = iter.next() addElem(next.key, next.hash) @@ -127,6 +134,14 @@ final class HashSet[A](initialCapacity: Int, loadFactor: Double) if (size == 0) return this } this + case lhs: mutable.LinkedHashSet[A] => + val iter = lhs.entryIterator + while (iter.hasNext) { + val next = iter.next() + remove(next.key, next.hash) + if (size == 0) return this + } + this case _ => super.subtractAll(xs) } } diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index 1853ed30759b..d84a5a9a526e 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -16,6 +16,7 @@ package mutable import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable +import scala.util.hashing.MurmurHash3 /** This class implements mutable maps using a hashtable. @@ -90,6 +91,10 @@ class LinkedHashMap[K, V] if (e == null) None else Some(e.value) } + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / LinkedHashMap.defaultLoadFactor).toInt) + if(target > table.length) growTable(target) + } override def contains(key: K): Boolean = { if (getClass eq classOf[LinkedHashMap[_, _]]) @@ -110,6 +115,41 @@ class LinkedHashMap[K, V] case nd => Some(nd.value) } + override def getOrElse[V1 >: V](key: K, default: => V1): V1 = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.getOrElse(key, default) + } else { + // .. but in the common case, we can avoid the Option boxing. + val nd = findEntry(key) + if (nd eq null) default else nd.value + } + } + + override def getOrElseUpdate(key: K, defaultValue: => V): V = { + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.getOrElseUpdate(key, defaultValue) + } else { + val hash = computeHash(key) + val idx = index(hash) + val nd = table(idx) match { + case null => null + case nd => nd.findEntry(key, hash) + } + if (nd != null) nd.value + else { + val table0 = table + val default = defaultValue + if (contentSize + 1 >= threshold) growTable(table.length * 2) + // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. + val newIdx = if (table0 eq table) idx else index(hash) + put0(key, default, false, hash, newIdx) + default + } + } + } + private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) /** Removes a key from this map if it exists @@ -150,6 +190,7 @@ class LinkedHashMap[K, V] @`inline` private[this] def improveHash(originalHash: Int): Int = { originalHash ^ (originalHash >>> 16) } + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) /** Computes the improved hash of this key */ @`inline` private[this] def computeHash(o: K): Int = improveHash(o.##) @@ -196,57 +237,73 @@ class LinkedHashMap[K, V] else Iterator.empty.next() } + private[collection] def entryIterator: Iterator[Entry] = new AbstractIterator[Entry] { + private[this] var cur = firstEntry + + def hasNext = cur ne null + + def next() = + if (hasNext) { + val res = cur; cur = cur.later; res + } + else Iterator.empty.next() + } // Override updateWith for performance, so we can do the update while hashing // the input key only once and performing one lookup into the hash table override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { - val hash = computeHash(key) - val indexedHash = index(hash) + if (getClass != classOf[LinkedHashMap[_, _]]) { + // subclasses of LinkedHashMap might customise `get` ... + super.updateWith(key)(remappingFunction) + } else { + val hash = computeHash(key) + val indexedHash = index(hash) + + var foundEntry: Entry = null + var previousEntry: Entry = null + table(indexedHash) match { + case null => + case nd => + @tailrec + def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { + if (h == nd.hash && k == nd.key) { + previousEntry = prev + foundEntry = nd + } + else if ((nd.next eq null) || (nd.hash > h)) () + else findEntry(nd, nd.next, k, h) + } - var foundEntry: Entry = null - var previousEntry: Entry = null - table(indexedHash) match { - case null => - case nd => - @tailrec - def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { - if (h == nd.hash && k == nd.key) { - previousEntry = prev - foundEntry = nd + findEntry(null, nd, key, hash) } - else if ((nd.next eq null) || (nd.hash > h)) () - else findEntry(nd, nd.next, k, h) - } - - findEntry(null, nd, key, hash) - } - val previousValue = foundEntry match { - case null => None - case nd => Some(nd.value) - } + val previousValue = foundEntry match { + case null => None + case nd => Some(nd.value) + } - val nextValue = remappingFunction(previousValue) + val nextValue = remappingFunction(previousValue) - (previousValue, nextValue) match { - case (None, None) => // do nothing + (previousValue, nextValue) match { + case (None, None) => // do nothing - case (Some(_), None) => - if (previousEntry != null) previousEntry.next = foundEntry.next - else table(indexedHash) = foundEntry.next - deleteEntry(foundEntry) - contentSize -= 1 + case (Some(_), None) => + if (previousEntry != null) previousEntry.next = foundEntry.next + else table(indexedHash) = foundEntry.next + deleteEntry(foundEntry) + contentSize -= 1 - case (None, Some(value)) => - val newIndexedHash = - if (contentSize + 1 >= threshold) { - growTable(table.length * 2) - index(hash) - } else indexedHash - put0(key, value, false, hash, newIndexedHash) + case (None, Some(value)) => + val newIndexedHash = + if (contentSize + 1 >= threshold) { + growTable(table.length * 2) + index(hash) + } else indexedHash + put0(key, value, false, hash, newIndexedHash) - case (Some(_), Some(newValue)) => foundEntry.value = newValue + case (Some(_), Some(newValue)) => foundEntry.value = newValue + } + nextValue } - nextValue } override def valuesIterator: Iterator[V] = new AbstractIterator[V] { @@ -257,6 +314,7 @@ class LinkedHashMap[K, V] else Iterator.empty.next() } + override def foreach[U](f: ((K, V)) => U): Unit = { var cur = firstEntry while (cur ne null) { @@ -394,6 +452,33 @@ class LinkedHashMap[K, V] } } + override def hashCode(): Int = { + abstract class LinkedHashMapIterator[A](val firstentry: Entry) extends AbstractIterator[A] { + var cur = firstentry + def extract(nd: Entry): A + def hasNext: Boolean = cur ne null + def next(): A = + if(hasNext) { + val r = extract(cur) + cur = cur.later + r + } else Iterator.empty.next() + } + + if (isEmpty) MurmurHash3.emptyMapHash + else { + val tupleHashIterator = new LinkedHashMapIterator[Any](firstEntry) { + var hash: Int = 0 + override def hashCode: Int = hash + override def extract(nd: Entry): Any = { + hash = MurmurHash3.tuple2Hash(unimproveHash(nd.hash), nd.value.##) + this + } + } + + MurmurHash3.orderedHash(tupleHashIterator, MurmurHash3.mapSeed) + } + } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "LinkedHashMap" } diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index 115ed2fb7e3f..39d5a84579e7 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -16,6 +16,7 @@ package mutable import scala.annotation.{nowarn, tailrec} import scala.collection.generic.DefaultSerializable +import scala.util.hashing.MurmurHash3 /** This class implements mutable sets using a hashtable. * The iterator and all traversal methods of this class visit elements in the order they were inserted. @@ -106,6 +107,17 @@ class LinkedHashSet[A] if (hasNext) { val res = cur.key; cur = cur.later; res } else Iterator.empty.next() } + private[collection] def entryIterator: Iterator[Entry] = new AbstractIterator[Entry] { + private[this] var cur = firstEntry + + def hasNext = cur ne null + + def next() = + if (hasNext) { + val res = cur; cur = cur.later; res + } + else Iterator.empty.next() + } override def foreach[U](f: A => U): Unit = { var cur = firstEntry @@ -131,6 +143,8 @@ class LinkedHashSet[A] originalHash ^ (originalHash >>> 16) } + @`inline` private[collection] def unimproveHash(improvedHash: Int): Int = improveHash(improvedHash) + /** Computes the improved hash of this key */ @`inline` private[this] def computeHash(o: A): Int = improveHash(o.##) @@ -270,6 +284,33 @@ class LinkedHashSet[A] } } + override def hashCode(): Int = { + abstract class LinkedHashSetIterator[B](val firstentry: Entry) extends AbstractIterator[B] { + var cur = firstentry + def extract(nd: Entry): B + def hasNext: Boolean = cur ne null + def next(): B = + if (hasNext) { + val r = extract(cur) + cur = cur.later + r + } else Iterator.empty.next() + } + + val setHashIterator = if (isEmpty) this.iterator + else { + new LinkedHashSetIterator[Any](firstEntry) { + var hash: Int = 0 + override def hashCode: Int = hash + override def extract(nd: Entry): Any = { + hash = unimproveHash(nd.hash) + this + } + } + } + MurmurHash3.orderedHash(setHashIterator, MurmurHash3.setSeed) + } + @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") override protected[this] def stringPrefix = "LinkedHashSet" } From ac301f2caf2c13be71772eebc9bc3db0ab826060 Mon Sep 17 00:00:00 2001 From: Francesco Kriegel Date: Fri, 16 Dec 2022 10:19:01 +0100 Subject: [PATCH 104/261] Fixed a bug in method 'diff' in class 'scala.collection.immutable.BitSet.BitSetN' and aligned similar method 'filterImpl' --- .../scala/collection/immutable/BitSet.scala | 106 ++++++++++-------- .../scala/collection/immutable/SetTest.scala | 13 +++ 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/library/scala/collection/immutable/BitSet.scala b/src/library/scala/collection/immutable/BitSet.scala index 4e8c4ee1a244..cc99f9907078 100644 --- a/src/library/scala/collection/immutable/BitSet.scala +++ b/src/library/scala/collection/immutable/BitSet.scala @@ -244,36 +244,45 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { anyChanges ||= currentWord != oldWord i -= 1 } - if (i < 0) { - // all indices >= 0 have had result 0, so the bitset is empty - this.empty - } else { - val minimumNonZeroIndex: Int = i + 1 - while (!anyChanges && i >= 0) { - val oldWord = word(i) - currentWord = oldWord & ~bs.word(i) - anyChanges ||= currentWord != oldWord - i -= 1 - } - if (anyChanges) { - if (minimumNonZeroIndex == -1) { - this.empty - } else if (minimumNonZeroIndex == 0) { - new BitSet1(currentWord) - } else if (minimumNonZeroIndex == 1) { - new BitSet2(word(0) & ~bs.word(0), currentWord) + i match { + case -1 => + if (anyChanges) { + if (currentWord == 0) { + this.empty + } else { + new BitSet1(currentWord) + } } else { + this + } + case 0 => + val oldFirstWord = word(0) + val firstWord = oldFirstWord & ~bs.word(0) + anyChanges ||= firstWord != oldFirstWord + if (anyChanges) { + new BitSet2(firstWord, currentWord) + } else { + this + } + case _ => + val minimumNonZeroIndex: Int = i + 1 + while (!anyChanges && i >= 0) { + val oldWord = word(i) + currentWord = oldWord & ~bs.word(i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { val newArray = elems.take(minimumNonZeroIndex + 1) newArray(i + 1) = currentWord while (i >= 0) { newArray(i) = word(i) & ~bs.word(i) i -= 1 } - this.fromBitMaskNoCopy(newArray) + new BitSetN(newArray) + } else { + this } - } else { - this - } } } else { var i = bsnwords - 1 @@ -314,36 +323,45 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] { anyChanges ||= currentWord != oldWord i -= 1 } - if (i < 0) { - // all indices >= 0 have had result 0, so the bitset is empty - if (currentWord == 0) this.empty else this.fromBitMaskNoCopy(Array(currentWord)) - } else { - val minimumNonZeroIndex: Int = i + 1 - while (!anyChanges && i >= 0) { - val oldWord = word(i) - currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i) - anyChanges ||= currentWord != oldWord - i -= 1 - } - if (anyChanges) { - if (minimumNonZeroIndex == -1) { - this.empty - } else if (minimumNonZeroIndex == 0) { - new BitSet1(currentWord) - } else if (minimumNonZeroIndex == 1) { - new BitSet2(BitSetOps.computeWordForFilter(pred, isFlipped, word(0), 0), currentWord) + i match { + case -1 => + if (anyChanges) { + if (currentWord == 0) { + this.empty + } else { + new BitSet1(currentWord) + } } else { + this + } + case 0 => + val oldFirstWord = word(0) + val firstWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldFirstWord, 0) + anyChanges ||= firstWord != oldFirstWord + if (anyChanges) { + new BitSet2(firstWord, currentWord) + } else { + this + } + case _ => + val minimumNonZeroIndex: Int = i + 1 + while (!anyChanges && i >= 0) { + val oldWord = word(i) + currentWord = BitSetOps.computeWordForFilter(pred, isFlipped, oldWord, i) + anyChanges ||= currentWord != oldWord + i -= 1 + } + if (anyChanges) { val newArray = elems.take(minimumNonZeroIndex + 1) newArray(i + 1) = currentWord while (i >= 0) { newArray(i) = BitSetOps.computeWordForFilter(pred, isFlipped, word(i), i) i -= 1 } - this.fromBitMaskNoCopy(newArray) + new BitSetN(newArray) + } else { + this } - } else { - this - } } } diff --git a/test/junit/scala/collection/immutable/SetTest.scala b/test/junit/scala/collection/immutable/SetTest.scala index 736c1e7e2e81..6bc08f19d93c 100644 --- a/test/junit/scala/collection/immutable/SetTest.scala +++ b/test/junit/scala/collection/immutable/SetTest.scala @@ -160,4 +160,17 @@ class SetTest { testCorrectness() testNoHashCodeInvocationsDuringSubsetOf() } + + @Test def pr10238(): Unit = { + assertEquals(BitSet(0), BitSet(0, 128) diff BitSet(128)) + + val us = scala.collection.immutable.BitSet(39, 41, 44, 46, 256) + val vs = scala.collection.immutable.BitSet(39, 41, 44, 46, 64, 256) + val xs = scala.collection.immutable.BitSet.fromBitMask(us.toBitMask.take(3)) + val ys = scala.collection.immutable.BitSet.fromBitMask(vs.toBitMask.take(3)) + val diff = ys diff xs + val expected = scala.collection.immutable.BitSet(64) + assertEquals(diff, expected) + } + } From f2b8dd6b7cba8417149a6298ce95c46615056ef9 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 16 Dec 2022 10:12:53 -0800 Subject: [PATCH 105/261] this is stable for patterns --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 8 +++----- test/files/pos/t8365.scala | 10 ++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 test/files/pos/t8365.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 460b5795ab72..52dfb25d6028 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1388,7 +1388,7 @@ self => * }}} */ def stableId(): Tree = - path(thisOK = false, typeOK = false) + path(thisOK = true, typeOK = false) /** {{{ * QualId ::= Id {`.` Id} @@ -2246,10 +2246,8 @@ self => * * XXX: Hook for IDE */ - def simplePattern(): Tree = ( - // simple diagnostics for this entry point - simplePattern(() => syntaxErrorOrIncompleteAnd("illegal start of simple pattern", skipIt = true)(errorPatternTree)) - ) + def simplePattern(): Tree = + simplePattern(() => syntaxErrorOrIncompleteAnd("illegal start of simple pattern", skipIt = true)(errorPatternTree)) // simple diagnostics for this entry point def simplePattern(onError: () => Tree): Tree = { val start = in.offset in.token match { diff --git a/test/files/pos/t8365.scala b/test/files/pos/t8365.scala new file mode 100644 index 000000000000..5f7547790e5f --- /dev/null +++ b/test/files/pos/t8365.scala @@ -0,0 +1,10 @@ + +class A { + def f = + this match { + case this => + } + val this = this +} + + From 201c3f55b05976c8a64cdfaa5c9bfa97c609375a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 18 Dec 2022 03:48:53 -0800 Subject: [PATCH 106/261] Restore Mr Snytt as a resource --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2eaf532d97d6..3d8432a9b636 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ If you need some help with your PR at any time, please feel free to @-mention an | [`@retronym`](https://github.com/retronym) | 2.12.x branch, compiler performance, weird compiler bugs, lambdas | | [`@SethTisue`](https://github.com/SethTisue) | getting started, build, CI, community build, Jenkins, docs, library, REPL | | [`@dwijnand`](https://github.com/dwijnand) | pattern matcher, MiMa, partest | + | [`@som-snytt`](https://github.com/som-snytt) | warnings/lints/errors, REPL, compiler options, compiler internals, partest | | [`@Ichoran`](https://github.com/Ichoran) | collections library, performance | | [`@viktorklang`](https://github.com/viktorklang) | concurrency, futures | | [`@sjrd`](https://github.com/sjrd) | interactions with Scala.js | From bdb5c1ff424d7b80689770a63083a2550be79d7e Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 19 Dec 2022 11:42:39 +0100 Subject: [PATCH 107/261] fix typo in recently added ambiguity message --- .../scala/tools/nsc/typechecker/Contexts.scala | 2 +- test/files/neg/t11921-alias.check | 8 ++++---- test/files/neg/t11921.check | 2 +- test/files/neg/t11921b.check | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6bcd81deb4df..47d8b5a25045 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1369,7 +1369,7 @@ trait Contexts { self: Analyzer => val inherit = if (parent.exists && parent != other.owner) s", inherited through parent $parent" else "" val message = s"""it is both defined in the enclosing ${sym.owner} and available in the enclosing $enclDesc as $other (defined in ${other.ownsString}$inherit) - |Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. + |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. |To continue using the symbol from the superclass, write `this.${sym.name}`.""".stripMargin if (currentRun.isScala3) Some(LookupAmbiguous(message)) diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check index db052d9ab7f0..a71a4f7a358c 100644 --- a/test/files/neg/t11921-alias.check +++ b/test/files/neg/t11921-alias.check @@ -1,27 +1,27 @@ t11921-alias.scala:18: warning: reference to TT is ambiguous; it is both defined in the enclosing object O and available in the enclosing class D as type TT (defined in class C) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.TT`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n(x: TT) = x // ambiguous ^ t11921-alias.scala:38: warning: reference to c is ambiguous; it is both defined in the enclosing class B and available in the enclosing anonymous class as value c (defined in class A) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.c`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n = c // ambiguous ^ t11921-alias.scala:57: warning: reference to name is ambiguous; it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class C) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.name`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) ^ t11921-alias.scala:67: warning: reference to name is ambiguous; it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.name`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) diff --git a/test/files/neg/t11921.check b/test/files/neg/t11921.check index 9a63050f0ffb..6a9275f1fab0 100644 --- a/test/files/neg/t11921.check +++ b/test/files/neg/t11921.check @@ -5,7 +5,7 @@ t11921.scala:6: error: type mismatch; ^ t11921.scala:6: warning: reference to coll is ambiguous; it is both defined in the enclosing method lazyMap and available in the enclosing anonymous class as method coll (defined in trait Iterable) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.coll`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def iterator = coll.iterator.map(f) // coll is ambiguous diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 5311512794dd..9c1ad36598e7 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -1,34 +1,34 @@ t11921b.scala:11: warning: reference to x is ambiguous; it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ t11921b.scala:15: warning: reference to x is ambiguous; it is both defined in the enclosing object Test and available in the enclosing anonymous class as value x (defined in class C) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ t11921b.scala:26: warning: reference to y is ambiguous; it is both defined in the enclosing method c and available in the enclosing anonymous class as value y (defined in class D) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.y`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ t11921b.scala:38: warning: reference to y is ambiguous; it is both defined in the enclosing method c and available in the enclosing class E as value y (defined in class D) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.y`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ t11921b.scala:75: warning: reference to x is ambiguous; it is both defined in the enclosing object Uhu and available in the enclosing class C as value x (defined in class A, inherited through parent class B) -Since 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def t = x // ambiguous, message mentions parent B From e32d206c5478e58f7ca16ef3d9f0636943c02a32 Mon Sep 17 00:00:00 2001 From: Simon R Date: Mon, 19 Dec 2022 16:43:13 +0100 Subject: [PATCH 108/261] Add Scala 3 link to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3d8432a9b636..ff367a04dc32 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ This is the home of the [Scala 2](https://www.scala-lang.org) standard library, compiler, and language spec. +If you want to visit the Scala 3 repository, go to the [lampepfl/dotty](https://github.com/lampepfl/dotty). + # How to contribute Issues and bug reports for Scala 2 are located in [scala/bug](https://github.com/scala/bug). That tracker is also where new contributors may find issues to work on: [good first issues](https://github.com/scala/bug/labels/good%20first%20issue), [help wanted](https://github.com/scala/bug/labels/help%20wanted). From c0beb39c70a803c3db74830d52bd31a312177d06 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 21 Dec 2022 13:56:53 +0100 Subject: [PATCH 109/261] Fix sealed suptype enumeration in match analysis Use the the approximated scrutinee type only to compare possible subtypes, but don't extract the prefix from it. --- .../nsc/transform/patmat/MatchAnalysis.scala | 4 ++-- test/files/neg/t12704.check | 7 +++++++ test/files/neg/t12704.scala | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t12704.check create mode 100644 test/files/neg/t12704.scala diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 99aafbee6a03..3b5b05b2bdba 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -110,7 +110,7 @@ trait TreeAndTypeAnalysis extends Debugging { private def enumerateSealed(tp: Type, grouped: Boolean): List[List[Type]] = { val tpApprox = analyzer.approximateAbstracts(tp) - val pre = tpApprox.prefix + val pre = tp.prefix val sym = tp.typeSymbol def subclassesToSubtypes(syms: List[Symbol]): List[Type] = syms.flatMap { sym => @@ -120,7 +120,7 @@ trait TreeAndTypeAnalysis extends Debugging { // sealed trait X[T]; class XInt extends X[Int] // XInt not valid when enumerating X[String] // however, must also approximate abstract types - val memberType = nestedMemberType(sym, pre, tpApprox.typeSymbol.owner) + val memberType = nestedMemberType(sym, pre, tp.typeSymbol.owner) val subTp = appliedType(memberType, WildcardType.fillList(sym.typeParams.length)) val subTpApprox = analyzer.approximateAbstracts(subTp) if (subTpApprox <:< tpApprox) Some(checkableType(subTp)) else None diff --git a/test/files/neg/t12704.check b/test/files/neg/t12704.check new file mode 100644 index 000000000000..2de8a06ca4df --- /dev/null +++ b/test/files/neg/t12704.check @@ -0,0 +1,7 @@ +t12704.scala:13: warning: match may not be exhaustive. +It would fail on the following input: L(_) + def t2(t: P) = t match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12704.scala b/test/files/neg/t12704.scala new file mode 100644 index 000000000000..db43b888d029 --- /dev/null +++ b/test/files/neg/t12704.scala @@ -0,0 +1,16 @@ +// scalac: -Werror +class Out { + sealed trait P + case class K(x: Int) extends P + case class L(x: Int) extends P +} +class C[O <: Out](val o: O) { + import o._ + def t1(t: P) = t match { + case _: K => 0 + case _: L => 0 + } + def t2(t: P) = t match { + case _: K => 0 + } +} From 84bf5cabe88921e9b34b3cdea1760d885effa318 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 16 Dec 2022 11:05:50 +0100 Subject: [PATCH 110/261] Don't GLB binders of type patterns, use the type directly In type patterns `c match { case x: T }`, the translation would assign the GLB of `c`'s type and `T` to the varaible `x`. This seems to trace back to the first version of the "virtual pattern matcher". I could not find a similar use of `glb` in that revision of the codebase. So I'm not sure if it was a new addition, or picked up from the previous implementation. https://github.com/scala/scala/blob/8a9fd64129926eea35f7dca181242855f14e153f/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala#L438-L440 In the test case, the GLB collapsed to `Null` because its computation failed (combination of f-bounds, existentials, skolems that I don't follow), see `throw GlbFailure`. This is how it's been for 14 years (b894f804ad). This resulted in a cast to `Null$` failing at runtime. I assume GLB is fixed in Scala 3, as this core of the type system has a new implementation. But the test case as such doesn't compile in Scala 3 due to the non-wildcard existential. --- .../transform/patmat/MatchTranslation.scala | 5 ++-- test/files/run/t12702.check | 2 ++ test/files/run/t12702.scala | 17 +++++++++++ .../tools/nsc/symtab/SymbolTableTest.scala | 29 +++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t12702.check create mode 100644 test/files/run/t12702.scala diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index d88456829b73..1097b6646e5a 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -68,11 +68,10 @@ trait MatchTranslation { def pos = tree.pos def tpe = binder.info.dealias // the type of the variable bound to the pattern def pt = unbound match { - case Star(tpt) => this glbWith seqType(tpt.tpe) + case Star(tpt) => seqType(tpt.tpe) case TypeBound(tpe) => tpe case tree => tree.tpe } - def glbWith(other: Type) = glb(tpe :: other :: Nil).normalize object SymbolAndTypeBound { def unapply(tree: Tree): Option[(Symbol, Type)] = tree match { @@ -94,7 +93,7 @@ trait MatchTranslation { private def bindingStep(sub: Symbol, subpattern: Tree) = step(SubstOnlyTreeMaker(sub, binder))(rebindTo(subpattern)) private def equalityTestStep() = step(EqualityTestTreeMaker(binder, tree, pos))() - private def typeTestStep(sub: Symbol, subPt: Type) = step(TypeTestTreeMaker(sub, binder, subPt, glbWith(subPt))(pos))() + private def typeTestStep(sub: Symbol, subPt: Type) = step(TypeTestTreeMaker(sub, binder, subPt, subPt)(pos))() private def alternativesStep(alts: List[Tree]) = step(AlternativesTreeMaker(binder, translatedAlts(alts), alts.head.pos))() private def translatedAlts(alts: List[Tree]) = alts map (alt => rebindTo(alt).translate()) private def noStep() = step()() diff --git a/test/files/run/t12702.check b/test/files/run/t12702.check new file mode 100644 index 000000000000..fd4efe73272d --- /dev/null +++ b/test/files/run/t12702.check @@ -0,0 +1,2 @@ +warning: 1 feature warning; re-run with -feature for details +IOS diff --git a/test/files/run/t12702.scala b/test/files/run/t12702.scala new file mode 100644 index 000000000000..8b9ba135a3c2 --- /dev/null +++ b/test/files/run/t12702.scala @@ -0,0 +1,17 @@ +object Test { + trait MFSS[X <: MFSS[_]] + trait CS extends MFSS[CS] + trait MFI { type ST } + case class MFSD[S](mFI: MFI {type ST = S}) + case object IOS extends MFI { type ST = CS } + type SD = MFSD[S] forSome { + type S <: MFSS[S] + } + def bad(sd: SD) = sd.mFI match { + case ios: IOS.type => println(ios) + } + def main(args: Array[String]): Unit = { + val x = MFSD(IOS) + bad(x) + } +} diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala index 8df5eb34eaf0..3f95929166ef 100644 --- a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala @@ -49,4 +49,33 @@ class SymbolTableTest { import symbolTable._ assertEquals(NoSymbol, NoSymbol.outerClass) } + + @Test def t12702_glb(): Unit = { + import symbolTable._ + import SymbolTableTest.t12702._ + val t1 = typeOf[IOS.type] + val t2 = { + val sd = typeOf[SD] + sd.memberType(sd.member(TermName("mFI"))).finalResultType + } + // t1: Test.IOS.type + // t2: Test.MFI{type +ST = S} + + // Ends up in `throw GlbFailure` in glb => Null + assertTrue(definitions.NullTpe =:= glb(t1 :: t2 :: Nil)) + } +} + +object SymbolTableTest { + object t12702 { + import scala.language.existentials + trait MFSS[X <: MFSS[_]] + trait CS extends MFSS[CS] + trait MFI { type ST } + case class MFSD[S](mFI: MFI {type ST = S}) + case object IOS extends MFI { type ST = CS } + type SD = MFSD[S] forSome { + type S <: MFSS[S] + } + } } From 62578d14d874ce20f3ad74fb949897dc9e3a5a68 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 14 Dec 2022 14:52:27 +0100 Subject: [PATCH 111/261] Use unorderedHash, LinkedHashMap/Set are equals to other Maps Plus review cleanups. Reuse a single iterator implementation. --- project/MimaFilters.scala | 4 + .../scala/collection/mutable/HashMap.scala | 12 +- .../collection/mutable/LinkedHashMap.scala | 192 ++++++++---------- .../collection/mutable/LinkedHashSet.scala | 45 ++-- 4 files changed, 115 insertions(+), 138 deletions(-) diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index b0c5c8b96823..f2cf65569fc3 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -44,6 +44,10 @@ object MimaFilters extends AutoPlugin { // private object used by methods ProblemFilters.exclude[MissingClassProblem]("scala.math.Numeric$BigDecimalIsConflicted$"), + + // PR 10235 + ProblemFilters.exclude[MissingClassProblem]("scala.collection.mutable.LinkedHashMap$LinkedHashMapIterator"), + ProblemFilters.exclude[MissingClassProblem]("scala.collection.mutable.LinkedHashSet$LinkedHashSetIterator"), ) override val buildSettings = Seq( diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala index 94f731e17967..160bbfb79d56 100644 --- a/src/library/scala/collection/mutable/HashMap.scala +++ b/src/library/scala/collection/mutable/HashMap.scala @@ -109,12 +109,12 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double) } this case lhm: mutable.LinkedHashMap[K, V] => - val iter = lhm.entryIterator - while (iter.hasNext) { - val entry = iter.next() - put0(entry.key, entry.value,entry.hash,getOld = false) - } - this + val iter = lhm.entryIterator + while (iter.hasNext) { + val entry = iter.next() + put0(entry.key, entry.value, entry.hash, getOld = false) + } + this case thatMap: Map[K, V] => thatMap.foreachEntry { (key: K, value: V) => put0(key, value, improveHash(key.##), getOld = false) diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index d84a5a9a526e..47c3c16f4163 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -93,7 +93,7 @@ class LinkedHashMap[K, V] } override def sizeHint(size: Int): Unit = { val target = tableSizeFor(((size + 1).toDouble / LinkedHashMap.defaultLoadFactor).toInt) - if(target > table.length) growTable(target) + if (target > table.length) growTable(target) } override def contains(key: K): Boolean = { @@ -131,23 +131,23 @@ class LinkedHashMap[K, V] // subclasses of LinkedHashMap might customise `get` ... super.getOrElseUpdate(key, defaultValue) } else { - val hash = computeHash(key) - val idx = index(hash) - val nd = table(idx) match { - case null => null - case nd => nd.findEntry(key, hash) - } - if (nd != null) nd.value - else { - val table0 = table - val default = defaultValue - if (contentSize + 1 >= threshold) growTable(table.length * 2) - // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. - val newIdx = if (table0 eq table) idx else index(hash) - put0(key, default, false, hash, newIdx) - default - } - } + val hash = computeHash(key) + val idx = index(hash) + val nd = table(idx) match { + case null => null + case nd => nd.findEntry(key, hash) + } + if (nd != null) nd.value + else { + val table0 = table + val default = defaultValue + if (contentSize + 1 >= threshold) growTable(table.length * 2) + // Avoid recomputing index if the `defaultValue()` or new element hasn't triggered a table resize. + val newIdx = if (table0 eq table) idx else index(hash) + put0(key, default, false, hash, newIdx) + default + } + } } private[this] def removeEntry0(elem: K): Entry = removeEntry0(elem, computeHash(elem)) @@ -215,39 +215,40 @@ class LinkedHashMap[K, V] this } - def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] { + private[this] abstract class LinkedHashMapIterator[T] extends AbstractIterator[T] { private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = (cur.key, cur.value); cur = cur.later; res } + def extract(nd: Entry): T + def hasNext: Boolean = cur ne null + def next(): T = + if (hasNext) { val r = extract(cur); cur = cur.later; r } else Iterator.empty.next() } + def iterator: Iterator[(K, V)] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[(K, V)] { + def extract(nd: Entry): (K, V) = (nd.key, nd.value) + } + protected class LinkedKeySet extends KeySet { override def iterableFactory: IterableFactory[collection.Set] = LinkedHashSet } override def keySet: collection.Set[K] = new LinkedKeySet - override def keysIterator: Iterator[K] = new AbstractIterator[K] { - private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = cur.key; cur = cur.later; res } - else Iterator.empty.next() - } + override def keysIterator: Iterator[K] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[K] { + def extract(nd: Entry): K = nd.key + } - private[collection] def entryIterator: Iterator[Entry] = new AbstractIterator[Entry] { - private[this] var cur = firstEntry + private[collection] def entryIterator: Iterator[Entry] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[Entry] { + def extract(nd: Entry): Entry = nd + } - def hasNext = cur ne null - def next() = - if (hasNext) { - val res = cur; cur = cur.later; res - } - else Iterator.empty.next() - } // Override updateWith for performance, so we can do the update while hashing // the input key only once and performing one lookup into the hash table override def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V] = { @@ -255,64 +256,62 @@ class LinkedHashMap[K, V] // subclasses of LinkedHashMap might customise `get` ... super.updateWith(key)(remappingFunction) } else { - val hash = computeHash(key) - val indexedHash = index(hash) - - var foundEntry: Entry = null - var previousEntry: Entry = null - table(indexedHash) match { - case null => - case nd => - @tailrec - def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { - if (h == nd.hash && k == nd.key) { - previousEntry = prev - foundEntry = nd - } - else if ((nd.next eq null) || (nd.hash > h)) () - else findEntry(nd, nd.next, k, h) - } - - findEntry(null, nd, key, hash) + val hash = computeHash(key) + val indexedHash = index(hash) + + var foundEntry: Entry = null + var previousEntry: Entry = null + table(indexedHash) match { + case null => + case nd => + @tailrec + def findEntry(prev: Entry, nd: Entry, k: K, h: Int): Unit = { + if (h == nd.hash && k == nd.key) { + previousEntry = prev + foundEntry = nd + } + else if ((nd.next eq null) || (nd.hash > h)) () + else findEntry(nd, nd.next, k, h) } - val previousValue = foundEntry match { - case null => None - case nd => Some(nd.value) - } + findEntry(null, nd, key, hash) + } + + val previousValue = foundEntry match { + case null => None + case nd => Some(nd.value) + } - val nextValue = remappingFunction(previousValue) + val nextValue = remappingFunction(previousValue) - (previousValue, nextValue) match { - case (None, None) => // do nothing + (previousValue, nextValue) match { + case (None, None) => // do nothing - case (Some(_), None) => - if (previousEntry != null) previousEntry.next = foundEntry.next - else table(indexedHash) = foundEntry.next - deleteEntry(foundEntry) - contentSize -= 1 + case (Some(_), None) => + if (previousEntry != null) previousEntry.next = foundEntry.next + else table(indexedHash) = foundEntry.next + deleteEntry(foundEntry) + contentSize -= 1 - case (None, Some(value)) => - val newIndexedHash = - if (contentSize + 1 >= threshold) { - growTable(table.length * 2) - index(hash) - } else indexedHash - put0(key, value, false, hash, newIndexedHash) + case (None, Some(value)) => + val newIndexedHash = + if (contentSize + 1 >= threshold) { + growTable(table.length * 2) + index(hash) + } else indexedHash + put0(key, value, false, hash, newIndexedHash) - case (Some(_), Some(newValue)) => foundEntry.value = newValue - } - nextValue + case (Some(_), Some(newValue)) => foundEntry.value = newValue + } + nextValue } } - override def valuesIterator: Iterator[V] = new AbstractIterator[V] { - private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = cur.value; cur = cur.later; res } - else Iterator.empty.next() - } + override def valuesIterator: Iterator[V] = + if (size == 0) Iterator.empty + else new LinkedHashMapIterator[V] { + def extract(nd: Entry): V = nd.value + } override def foreach[U](f: ((K, V)) => U): Unit = { @@ -452,31 +451,18 @@ class LinkedHashMap[K, V] } } - override def hashCode(): Int = { - abstract class LinkedHashMapIterator[A](val firstentry: Entry) extends AbstractIterator[A] { - var cur = firstentry - def extract(nd: Entry): A - def hasNext: Boolean = cur ne null - def next(): A = - if(hasNext) { - val r = extract(cur) - cur = cur.later - r - } else Iterator.empty.next() - } - + override def hashCode: Int = { if (isEmpty) MurmurHash3.emptyMapHash else { - val tupleHashIterator = new LinkedHashMapIterator[Any](firstEntry) { + val tupleHashIterator = new LinkedHashMapIterator[Any] { var hash: Int = 0 override def hashCode: Int = hash - override def extract(nd: Entry): Any = { + override def extract(nd: Entry): Any = { hash = MurmurHash3.tuple2Hash(unimproveHash(nd.hash), nd.value.##) this } } - - MurmurHash3.orderedHash(tupleHashIterator, MurmurHash3.mapSeed) + MurmurHash3.unorderedHash(tupleHashIterator, MurmurHash3.mapSeed) } } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index 39d5a84579e7..684a80cbc978 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -43,7 +43,7 @@ class LinkedHashSet[A] // stepper is not overridden to use XTableStepper because that stepper would not return the // elements in insertion order - type Entry = LinkedHashSet.Entry[A] + /*private*/ type Entry = LinkedHashSet.Entry[A] protected var firstEntry: Entry = null @@ -100,23 +100,21 @@ class LinkedHashSet[A] override def remove(elem: A): Boolean = remove0(elem, computeHash(elem)) - def iterator: Iterator[A] = new AbstractIterator[A] { + private[this] abstract class LinkedHashSetIterator[T] extends AbstractIterator[T] { private[this] var cur = firstEntry - def hasNext = cur ne null - def next() = - if (hasNext) { val res = cur.key; cur = cur.later; res } + def extract(nd: Entry): T + def hasNext: Boolean = cur ne null + def next(): T = + if (hasNext) { val r = extract(cur); cur = cur.later; r } else Iterator.empty.next() } - private[collection] def entryIterator: Iterator[Entry] = new AbstractIterator[Entry] { - private[this] var cur = firstEntry - def hasNext = cur ne null + def iterator: Iterator[A] = new LinkedHashSetIterator[A] { + override def extract(nd: Entry): A = nd.key + } - def next() = - if (hasNext) { - val res = cur; cur = cur.later; res - } - else Iterator.empty.next() + private[collection] def entryIterator: Iterator[Entry] = new LinkedHashSetIterator[Entry] { + override def extract(nd: Entry): Entry = nd } override def foreach[U](f: A => U): Unit = { @@ -284,22 +282,11 @@ class LinkedHashSet[A] } } - override def hashCode(): Int = { - abstract class LinkedHashSetIterator[B](val firstentry: Entry) extends AbstractIterator[B] { - var cur = firstentry - def extract(nd: Entry): B - def hasNext: Boolean = cur ne null - def next(): B = - if (hasNext) { - val r = extract(cur) - cur = cur.later - r - } else Iterator.empty.next() - } - - val setHashIterator = if (isEmpty) this.iterator + override def hashCode: Int = { + val setHashIterator = + if (isEmpty) this.iterator else { - new LinkedHashSetIterator[Any](firstEntry) { + new LinkedHashSetIterator[Any] { var hash: Int = 0 override def hashCode: Int = hash override def extract(nd: Entry): Any = { @@ -308,7 +295,7 @@ class LinkedHashSet[A] } } } - MurmurHash3.orderedHash(setHashIterator, MurmurHash3.setSeed) + MurmurHash3.unorderedHash(setHashIterator, MurmurHash3.setSeed) } @nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""") From e7f9194bee567e17c3c220b7c09cd4d90ee32f4d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 1 Mar 2022 01:45:23 -0800 Subject: [PATCH 112/261] Minor refactor in ListSet to foreground guards and add test --- build.sbt | 2 +- .../scala/collection/immutable/ListSet.scala | 25 ++++++++----------- .../collection/immutable/ListSetTest.scala | 16 +++++++++++- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/build.sbt b/build.sbt index 65d3538f807f..98b0d4ea2580 100644 --- a/build.sbt +++ b/build.sbt @@ -691,7 +691,7 @@ lazy val junit = project.in(file("test") / "junit") (Test / forkOptions) := (Test / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), (Test / testOnly / forkOptions) := (Test / testOnly / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), libraryDependencies ++= Seq(junitDep, junitInterfaceDep, jolDep), - testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"), + testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v", "-s"), (Compile / unmanagedSourceDirectories) := Nil, (Test / unmanagedSourceDirectories) := List(baseDirectory.value), Test / headerSources := Nil, diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index 338fca6c8d72..03466e11761a 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -82,18 +82,15 @@ sealed class ListSet[A] extends AbstractSet[A] override def ++(xs: GenTraversableOnce[A]): ListSet[A] = xs match { - // we want to avoid to use of iterator as it causes allocations - // during reverseList - case ls: ListSet[A] => - if (ls eq this) this - else { - val lsSize = ls.size - if (lsSize == 0) this - else if (isEmpty) ls + case _: this.type => this + case _ if xs.isEmpty => this + // we want to avoid using iterator as it causes allocations during reverseList + case ls: ListSet[A] => + if (isEmpty) ls else { - @tailrec def skip(ls: ListSet[A], count: Int): ListSet[A] = { + // optimize add non-empty ListSet + @tailrec def skip(ls: ListSet[A], count: Int): ListSet[A] = if (count == 0) ls else skip(ls.next, count - 1) - } @tailrec def containsLimited(n: ListSet[A], e: A, end: ListSet[A]): Boolean = (n ne end) && (e == n.elem || containsLimited(n.next, e, end)) @@ -104,6 +101,7 @@ sealed class ListSet[A] extends AbstractSet[A] // We hope to get some structural sharing so find the tail of the // ListSet that are `eq` (or if there are not any then the ends of the lists), // and we optimise the add to only iterate until we reach the common end + val lsSize = ls.size val thisSize = this.size val remaining = Math.min(thisSize, lsSize) var thisTail = skip(this, thisSize - remaining) @@ -151,16 +149,13 @@ sealed class ListSet[A] extends AbstractSet[A] case 4 => pending3 case _ => pending(pendingCount - 5) } - val r = result + val r = result result = new r.Node(elem) pendingCount -= 1 } result } - } - case _ => - if (xs.isEmpty) this - else (repr /: xs) (_ + _) + case _ => xs.foldLeft(repr)(_ + _) } def iterator: Iterator[A] = { diff --git a/test/junit/scala/collection/immutable/ListSetTest.scala b/test/junit/scala/collection/immutable/ListSetTest.scala index 4ce4fc5a6206..6163307e58e8 100644 --- a/test/junit/scala/collection/immutable/ListSetTest.scala +++ b/test/junit/scala/collection/immutable/ListSetTest.scala @@ -1,10 +1,12 @@ package scala.collection.immutable -import org.junit.Assert._ +import org.junit.Assert.{fail => _, _} import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import scala.tools.testing.AssertUtil._ + @RunWith(classOf[JUnit4]) class ListSetTest { @@ -56,6 +58,11 @@ class ListSetTest { @Test def hasCorrectOrderAfterPlusPlus(): Unit = { val foo = ListSet(1) + + assertSame(foo, foo ++ foo) + assertSame(foo, foo ++ ListSet.empty) + assertSame(foo, foo ++ Nil) + var bar = foo ++ ListSet() assertEquals(List(1), bar.iterator.toList) @@ -79,6 +86,13 @@ class ListSetTest { bar = foo ++ ListSet(1, 2, 3, 4, 5, 6) assertEquals(List(1, 2, 3, 4, 5, 6), bar.iterator.toList) + assertSameElements(List(1, 2, 3, 4, 5, 6), bar.iterator) + } + @Test + def `t12316 ++ is correctly ordered`(): Unit = { + // was: ListSet(1, 2, 3, 42, 43, 44, 29, 28, 27, 12, 11, 10) + assertEquals(ListSet(1,2,3,42,43,44,10,11,12,27,28,29), ListSet(1,2,3,42,43,44) ++ ListSet(10,11,12,42,43,44,27,28,29)) + assertSameElements(List(1,2,3,42,43,44,10,11,12,27,28,29), ListSet(1,2,3,42,43,44) ++ ListSet(10,11,12,42,43,44,27,28,29)) } @Test From b51739da86413126c70410bc4aa0f2d967a3455a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 24 Oct 2018 17:50:42 -0700 Subject: [PATCH 113/261] Runner error status on bad class or jar name --- .../tools/nsc/GenericRunnerCommand.scala | 2 +- .../scala/tools/nsc/MainGenericRunner.scala | 7 ++++- .../scala/tools/nsc/MainRunnerTest.scala | 26 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 test/junit/scala/tools/nsc/MainRunnerTest.scala diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala index 1a1b9301404a..a6af42fe5e7a 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala @@ -45,7 +45,7 @@ extends CompilerCommand(args, settings) { val f = io.File(target) if (!f.hasExtension("class", "jar", "zip") && f.canRead) AsScript else { - Console.err.println("No such file or class on classpath: " + target) + settings.errorFn("No such file or class on classpath: " + target) Error } } diff --git a/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala b/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala index edb000bfc88f..96151010f317 100644 --- a/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala +++ b/src/repl-frontend/scala/tools/nsc/MainGenericRunner.scala @@ -44,6 +44,7 @@ class MainGenericRunner { def process(args: Array[String]): Boolean = { val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x)) import command.{settings, howToRun, thingToRun, shortUsageMsg} + import MainGenericRunner.CommandFailure // only created for info message def sampleCompiler = new Global(settings) @@ -75,7 +76,7 @@ class MainGenericRunner { case AsJar => JarRunner.runJar(settings, thingToRun, command.arguments) case Error => - None + Some(CommandFailure) case _ => // We start the repl when no arguments are given. if (settings.Wconf.isDefault && settings.lint.isDefault) { @@ -90,6 +91,7 @@ class MainGenericRunner { runTarget() match { case Some(ScriptCompileError) => false + case Some(CommandFailure) => false case e @ Some(ex) => errorFn("", e) case _ => true } @@ -105,5 +107,8 @@ class MainGenericRunner { } object MainGenericRunner extends MainGenericRunner { + // control indicating command ran but non-zero exit + object CommandFailure extends scala.util.control.ControlThrowable("Command failed") + def main(args: Array[String]): Unit = if (!process(args)) System.exit(1) } diff --git a/test/junit/scala/tools/nsc/MainRunnerTest.scala b/test/junit/scala/tools/nsc/MainRunnerTest.scala new file mode 100644 index 000000000000..5520a8d9bf4e --- /dev/null +++ b/test/junit/scala/tools/nsc/MainRunnerTest.scala @@ -0,0 +1,26 @@ +package scala.tools.nsc + +import org.junit.Assert._ +import org.junit.Test + +class MainRunnerTest { + @Test + def `command reports no class`: Unit = { + import GenericRunnerCommand._ + var message: String = "" + assertEquals(Error, new GenericRunnerCommand(List("Junk"), err => message = err).howToRun) + assertEquals("No such file or class on classpath: Junk", message) + } + object TestGenericRunner extends MainGenericRunner { + override def errorFn(str: String, e: Option[Throwable], isFailure: Boolean): Boolean = { + assertTrue(isFailure) + !isFailure + } + } + @Test + def `scala Junk should fail`: Unit = + assertFalse(TestGenericRunner.process(Array("Junk"))) + @Test + def `scala junk.jar should fail`: Unit = + assertFalse(TestGenericRunner.process(Array("junk.jar"))) +} From 70862c43c760957cc4e28ec1ab6673be637d673d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 3 Jan 2023 11:24:13 -0800 Subject: [PATCH 114/261] Avoid name check when loading repl products --- .../scala/tools/nsc/interpreter/IMain.scala | 16 +++++++++++---- test/files/run/t12705.check | 11 ++++++++++ test/files/run/t12705.scala | 20 +++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 test/files/run/t12705.check create mode 100644 test/files/run/t12705.scala diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index b21ea2a9459b..cc9374f761a5 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -347,15 +347,23 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade override def translateEnclosingClass(n: String): Option[String] = symbolOfTerm(n).enclClass.toOption map flatPath /** If unable to find a resource foo.class, try taking foo as a symbol in scope - * and use its java class name as a resource to load. - * - * \$intp.classLoader classBytes "Bippy" or \$intp.classLoader getResource "Bippy.class" just work. - */ + * and use its java class name as a resource to load. + * + * \$intp.classLoader classBytes "Bippy" or \$intp.classLoader getResource "Bippy.class" just work. + */ private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(replOutput.dir, parent) { override protected def findAbstractFile(name: String): AbstractFile = super.findAbstractFile(name) match { case null if _initializeComplete => translateSimpleResource(name).map(super.findAbstractFile).orNull case file => file } + // if the name was mapped by findAbstractFile, supply null name to avoid name check in defineClass + override protected def findClass(name: String): Class[_] = { + val bytes = classBytes(name) + if (bytes.length == 0) + throw new ClassNotFoundException(name) + else + defineClass(/*name=*/null, bytes, 0, bytes.length, protectionDomain) + } } private def makeClassLoader(): AbstractFileClassLoader = new TranslatingClassLoader({ diff --git a/test/files/run/t12705.check b/test/files/run/t12705.check new file mode 100644 index 000000000000..3708fadad36c --- /dev/null +++ b/test/files/run/t12705.check @@ -0,0 +1,11 @@ + +scala> case class Person(name: String) +class Person + +scala> $intp.classLoader.getResource("Person.class") +val res0: java.net.URL = memory:(memory)/Person.class + +scala> $intp.classLoader.loadClass("Person") +val res1: Class[_] = class Person + +scala> :quit diff --git a/test/files/run/t12705.scala b/test/files/run/t12705.scala new file mode 100644 index 000000000000..f3532df82ccb --- /dev/null +++ b/test/files/run/t12705.scala @@ -0,0 +1,20 @@ + +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = + """|case class Person(name: String) + |$intp.classLoader.getResource("Person.class") + |$intp.classLoader.loadClass("Person")""".stripMargin + //|classOf[Person].getClassLoader.loadClass("Person")""".stripMargin +} + +/* +java.lang.NoClassDefFoundError: Person (wrong name: Person) + at java.base/java.lang.ClassLoader.defineClass1(Native Method) + at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013) + at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:77) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588) + at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) + ... 76 elided +*/ From 258a171c46a89b965e5913c0864dd898a611a334 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 5 Jan 2023 11:57:10 -0800 Subject: [PATCH 115/261] bump copyright year to 2023 in the usual places. based on 4257176, and verified with `git grep -w 2022` --- NOTICE | 4 ++-- doc/LICENSE.md | 4 ++-- doc/License.rtf | 4 ++-- project/VersionUtil.scala | 2 +- src/library/scala/util/Properties.scala | 2 +- src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala | 2 +- src/scalap/decoder.properties | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/NOTICE b/NOTICE index 611c177829c1..5bceee7b366a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,6 +1,6 @@ Scala -Copyright (c) 2002-2022 EPFL -Copyright (c) 2011-2022 Lightbend, Inc. +Copyright (c) 2002-2023 EPFL +Copyright (c) 2011-2023 Lightbend, Inc. Scala includes software developed at LAMP/EPFL (https://lamp.epfl.ch/) and diff --git a/doc/LICENSE.md b/doc/LICENSE.md index 150e946af7c3..8545299c0317 100644 --- a/doc/LICENSE.md +++ b/doc/LICENSE.md @@ -2,9 +2,9 @@ Scala is licensed under the [Apache License Version 2.0](https://www.apache.org/ ## Scala License -Copyright (c) 2002-2022 EPFL +Copyright (c) 2002-2023 EPFL -Copyright (c) 2011-2022 Lightbend, Inc. +Copyright (c) 2011-2023 Lightbend, Inc. All rights reserved. diff --git a/doc/License.rtf b/doc/License.rtf index cb188b135e65..cc2af02b3e5c 100644 --- a/doc/License.rtf +++ b/doc/License.rtf @@ -23,8 +23,8 @@ Scala is licensed under the\'a0{\field{\*\fldinst{HYPERLINK "https://www.apache. \fs48 \cf2 Scala License\ \pard\pardeftab720\sl360\sa320\partightenfactor0 -\f0\b0\fs28 \cf2 Copyright (c) 2002-2022 EPFL\ -Copyright (c) 2011-2022 Lightbend, Inc.\ +\f0\b0\fs28 \cf2 Copyright (c) 2002-2023 EPFL\ +Copyright (c) 2011-2023 Lightbend, Inc.\ All rights reserved.\ \pard\pardeftab720\sl360\sa320\partightenfactor0 \cf2 \cb4 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at {\field{\*\fldinst{HYPERLINK "http://www.apache.org/licenses/LICENSE-2.0"}}{\fldrslt http://www.apache.org/licenses/LICENSE-2.0}}.\ diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index b036143333b4..760023ea2ce8 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -30,7 +30,7 @@ object VersionUtil { ) lazy val generatePropertiesFileSettings = Seq[Setting[_]]( - copyrightString := "Copyright 2002-2022, LAMP/EPFL and Lightbend, Inc.", + copyrightString := "Copyright 2002-2023, LAMP/EPFL and Lightbend, Inc.", shellWelcomeString := """ | ________ ___ / / ___ | / __/ __// _ | / / / _ | diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index bd97fbcfa016..355a5f626d94 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -108,7 +108,7 @@ private[scala] trait PropertiesTrait { * or "version (unknown)" if it cannot be determined. */ val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") - val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2022, LAMP/EPFL and Lightbend, Inc.") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2023, LAMP/EPFL and Lightbend, Inc.") /** This is the encoding to use reading in source files, overridden with -encoding. * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala index f483f8e02dfb..998e4b5c056a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala @@ -407,7 +407,7 @@ trait EntityPage extends HtmlPage { { if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value)) - + else } diff --git a/src/scalap/decoder.properties b/src/scalap/decoder.properties index 290fe83e11bd..b5e00dc083fd 100644 --- a/src/scalap/decoder.properties +++ b/src/scalap/decoder.properties @@ -1,2 +1,2 @@ version.number=2.0.1 -copyright.string=(c) 2002-2022 LAMP/EPFL +copyright.string=(c) 2002-2023 LAMP/EPFL From 88afd4ac8a9260b639a39abb06122ca535b0794d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 5 Jan 2023 12:04:22 -0800 Subject: [PATCH 116/261] Infix type args dropped in Scala 3 --- .../scala/tools/nsc/ast/parser/Parsers.scala | 7 ++++++- test/files/neg/dotless-targs-a.check | 15 +++++++++++++++ test/files/neg/dotless-targs-a.scala | 10 ++++++++++ test/files/neg/dotless-targs-b.check | 10 ++++++++++ test/files/neg/dotless-targs-b.scala | 10 ++++++++++ test/files/neg/dotless-targs-ranged-a.check | 16 ++++++++++++++++ test/files/neg/dotless-targs-ranged-a.scala | 16 ++++++++++++++++ test/files/neg/dotless-targs.check | 2 +- test/files/neg/dotless-targs.scala | 2 ++ test/files/run/t10751.check | 1 + test/files/run/t1980.check | 1 + 11 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/dotless-targs-a.check create mode 100644 test/files/neg/dotless-targs-a.scala create mode 100644 test/files/neg/dotless-targs-b.check create mode 100644 test/files/neg/dotless-targs-b.scala create mode 100644 test/files/neg/dotless-targs-ranged-a.check create mode 100644 test/files/neg/dotless-targs-ranged-a.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 52dfb25d6028..ab125e793582 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -986,10 +986,15 @@ self => def finishBinaryOp(isExpr: Boolean, opinfo: OpInfo, rhs: Tree): Tree = { import opinfo._ + if (targs.nonEmpty) + if (currentRun.isScala3) + syntaxError(offset, "type application is not allowed for infix operators") + else + deprecationWarning(offset, "type application will be disallowed for infix operators", "2.13.11") val operatorPos: Position = Position.range(rhs.pos.source, offset, offset, offset + operator.length) val pos = lhs.pos.union(rhs.pos).union(operatorPos).withEnd(in.lastOffset).withPoint(offset) - atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, opinfo.targs)) + atPos(pos)(makeBinop(isExpr, lhs, operator, rhs, operatorPos, targs)) } def reduceExprStack(base: List[OpInfo], top: Tree): Tree = reduceStack(isExpr = true, base, top) diff --git a/test/files/neg/dotless-targs-a.check b/test/files/neg/dotless-targs-a.check new file mode 100644 index 000000000000..e16d2b2e4c70 --- /dev/null +++ b/test/files/neg/dotless-targs-a.check @@ -0,0 +1,15 @@ +dotless-targs-a.scala:4: warning: type application will be disallowed for infix operators + def fn2 = List apply[Int] 2 + ^ +dotless-targs-a.scala:9: warning: type application will be disallowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-a.scala:9: warning: type application will be disallowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-a.scala:9: warning: multiarg infix syntax looks like a tuple and will be deprecated + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +error: No warnings can be incurred under -Werror. +4 warnings +1 error diff --git a/test/files/neg/dotless-targs-a.scala b/test/files/neg/dotless-targs-a.scala new file mode 100644 index 000000000000..1112f676c2da --- /dev/null +++ b/test/files/neg/dotless-targs-a.scala @@ -0,0 +1,10 @@ +// scalac: -Werror -Xlint -Yrangepos:false +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} diff --git a/test/files/neg/dotless-targs-b.check b/test/files/neg/dotless-targs-b.check new file mode 100644 index 000000000000..6aafb4db9157 --- /dev/null +++ b/test/files/neg/dotless-targs-b.check @@ -0,0 +1,10 @@ +dotless-targs-b.scala:4: error: type application is not allowed for infix operators + def fn2 = List apply[Int] 2 + ^ +dotless-targs-b.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-b.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +3 errors diff --git a/test/files/neg/dotless-targs-b.scala b/test/files/neg/dotless-targs-b.scala new file mode 100644 index 000000000000..d54344238a9a --- /dev/null +++ b/test/files/neg/dotless-targs-b.scala @@ -0,0 +1,10 @@ +// scalac: -Werror -Xlint -Xsource:3 -Yrangepos:false +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) +} diff --git a/test/files/neg/dotless-targs-ranged-a.check b/test/files/neg/dotless-targs-ranged-a.check new file mode 100644 index 000000000000..870784528580 --- /dev/null +++ b/test/files/neg/dotless-targs-ranged-a.check @@ -0,0 +1,16 @@ +dotless-targs-ranged-a.scala:4: error: type application is not allowed for infix operators + def fn2 = List apply[Int] 2 + ^ +dotless-targs-ranged-a.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-ranged-a.scala:9: error: type application is not allowed for infix operators + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + ^ +dotless-targs-ranged-a.scala:13: error: type application is not allowed for infix operators + def eval = 1 ->[Int] 2 + ^ +dotless-targs-ranged-a.scala:14: error: type application is not allowed for infix operators + def evil = new A() op [Int, String ] 42 + ^ +5 errors diff --git a/test/files/neg/dotless-targs-ranged-a.scala b/test/files/neg/dotless-targs-ranged-a.scala new file mode 100644 index 000000000000..9e014c1f0386 --- /dev/null +++ b/test/files/neg/dotless-targs-ranged-a.scala @@ -0,0 +1,16 @@ +// scalac: -Xlint -Xsource:3 -Yrangepos:true +class A { + def fn1 = List apply 1 + def fn2 = List apply[Int] 2 + + def g1: Char = "g1" toList 0 + def g2: Char = "g2" apply 1 + + def h1 = List apply[List[Int]] (List(1), List(2)) mapConserve[List[Any]] (x => x) + + def op[A, B](i: Int): Int = 2*i + + def eval = 1 ->[Int] 2 + def evil = new A() op [Int, String ] 42 +} + diff --git a/test/files/neg/dotless-targs.check b/test/files/neg/dotless-targs.check index 4b22dd63e521..e85ded85bb4c 100644 --- a/test/files/neg/dotless-targs.check +++ b/test/files/neg/dotless-targs.check @@ -1,4 +1,4 @@ -dotless-targs.scala:2: error: type application is not allowed for postfix operators +dotless-targs.scala:4: error: type application is not allowed for postfix operators def f1 = "f1" isInstanceOf[String] // not ok ^ 1 error diff --git a/test/files/neg/dotless-targs.scala b/test/files/neg/dotless-targs.scala index eff63cbec4f9..70e01c9a00a4 100644 --- a/test/files/neg/dotless-targs.scala +++ b/test/files/neg/dotless-targs.scala @@ -1,3 +1,5 @@ +// scalac: -Xsource:3 -language:postfixOps +// class A { def f1 = "f1" isInstanceOf[String] // not ok def f2 = "f2".isInstanceOf[String] // ok diff --git a/test/files/run/t10751.check b/test/files/run/t10751.check index 0142b6896a14..84258d9452e2 100644 --- a/test/files/run/t10751.check +++ b/test/files/run/t10751.check @@ -35,3 +35,4 @@ } } +warning: 4 deprecations (since 2.13.11); re-run with -deprecation for details diff --git a/test/files/run/t1980.check b/test/files/run/t1980.check index aebd8425db7c..c4e42211b36c 100644 --- a/test/files/run/t1980.check +++ b/test/files/run/t1980.check @@ -1,3 +1,4 @@ +warning: 1 deprecation (since 2.13.11); re-run with -deprecation for details 1. defining foo 1 foo 2 From 638211ac0ffaa11682a80a8b3167285be1185f78 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 6 Jan 2023 12:37:14 +0100 Subject: [PATCH 117/261] update to Scala 3.2.1 (and add new error to check file) --- project/DottySupport.scala | 2 +- .../neg-full-circle/src-3-app/TestExperimentalDefsPre.check | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index 5fb86c005d87..a37605c08a1e 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.2.0" // TASTy version 28.2-0 + val supportedTASTyRelease = "3.2.1" // TASTy version 28.2-0 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease diff --git a/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check b/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check index a645c25a1366..346149d4cade 100644 --- a/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check +++ b/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check @@ -1,3 +1,7 @@ +-- Error: TestExperimentalDefsPre_fail.scala:1:18 +1 |import downstream.ExperimentalDefsPre.* + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |object ExperimentalDefsPre is marked @experimental and therefore may only be used in an experimental scope. -- Error: TestExperimentalDefsPre_fail.scala:4:10 4 | def test = new SubExperimentalNotExperimental | ^ @@ -14,4 +18,4 @@ 6 | class SubSubExperimental extends SubExperimentalNotExperimental | ^ |extension of experimental class SubExperimentalNotExperimental must have @experimental annotation -4 errors found +5 errors found From 91f899543edbc0b38d9ddebc0df3874bc16d8285 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 11 Jan 2023 13:22:10 +0100 Subject: [PATCH 118/261] [nomerge] Use newer dist on travis for spec build Recent builds failed with ``` $ ruby -v ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-linux] $ gem install bundler ERROR: Error installing bundler: The last version of bundler (>= 0) to support your Ruby & RubyGems was 2.3.26. Try installing it with `gem install bundler -v 2.3.26` bundler requires Ruby version >= 2.6.0. The current ruby version is 2.5.3.105. ``` We have the same in 2.13.x already for a different reason. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 63a500e4fabe..db6e68321df5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,8 @@ jobs: # build the spec using jekyll - stage: build + # bionic for newer ruby ("bundler requires Ruby version >= 2.6.0") + dist: bionic language: ruby install: - ruby -v From 3e5b20fae1464a1d7b9af9e0a743d201a88cbd71 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 11 Jan 2023 10:55:52 -0800 Subject: [PATCH 119/261] Tweak missing arg message with more names If not enough args, also report bad named args. Don't take leading bad named arg as positional, since assignment is no longer supported; instead, report the bad name. --- .../tools/nsc/typechecker/ContextErrors.scala | 7 +++---- .../tools/nsc/typechecker/NamesDefaults.scala | 19 ++++++++++--------- .../scala/tools/nsc/typechecker/Typers.scala | 5 ++++- test/files/neg/t8667.check | 18 +++++++++++++++++- test/files/neg/t8667.scala | 9 +++++++++ 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 6c1885b59516..29630cd2cb15 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -689,11 +689,10 @@ trait ContextErrors extends splain.SplainErrors { def NotEnoughArgsError(tree: Tree, fun: Tree, missing: List[Symbol]) = { val notEnoughArgumentsMsg = { val suffix = if (missing.isEmpty) "" else { - val keep = missing take 3 map (_.name) + val keep = missing.take(3).map(_.name) val ess = if (missing.tail.isEmpty) "" else "s" - f".%nUnspecified value parameter$ess ${ - keep.mkString("", ", ", if ((missing drop 3).nonEmpty) "..." else ".") - }" + val dots = if (missing.drop(3).nonEmpty) "..." else "." + keep.mkString(s".\nUnspecified value parameter$ess ", ", ", dots) } s"not enough arguments for ${ treeSymTypeMsg(fun) }$suffix" } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 0e169c0d80b9..941a36f318f0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -405,7 +405,7 @@ trait NamesDefaults { self: Analyzer => */ def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name]): (List[Symbol], Boolean) = { // The argument list contains first a mix of positional args and named args that are on the - // right parameter position, and then a number or named args on different positions. + // right parameter position, and then a number of named args on different positions. // collect all named arguments whose position does not match the parameter they define val namedArgsOnChangedPosition = args.zip(params) dropWhile { @@ -413,9 +413,8 @@ trait NamesDefaults { self: Analyzer => val n = argName(arg) // drop the argument if // - it's not named, or - // - it's named, but defines the parameter on its current position, or - // - it's named, but none of the parameter names matches (treated as a positional argument, an assignment expression) - n.isEmpty || n.get == param.name || params.forall(_.name != n.get) + // - it's named, but defines the parameter on its current position + n.isEmpty || n.get == param.name } map (_._1) val paramsWithoutPositionalArg = params.drop(args.length - namedArgsOnChangedPosition.length) @@ -433,6 +432,8 @@ trait NamesDefaults { self: Analyzer => * Extend the argument list `givenArgs` with default arguments. Defaults are added * as named arguments calling the corresponding default getter. * + * Returns the extended arg list and missing parameters if any. + * * Example: given * def foo(x: Int = 2, y: String = "def") * foo(y = "lt") @@ -443,8 +444,8 @@ trait NamesDefaults { self: Analyzer => pos: scala.reflect.internal.util.Position, context: Context): (List[Tree], List[Symbol]) = { if (givenArgs.length < params.length) { val (missing, positional) = missingParams(givenArgs, params, nameOfNamedArg) - if (missing forall (_.hasDefault)) { - val defaultArgs = missing flatMap (p => { + if (missing.forall(_.hasDefault)) { + val defaultArgs = missing flatMap { p => val defGetter = defaultGetter(p, context) // TODO #3649 can create spurious errors when companion object is gone (because it becomes unlinked from scope) if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649 @@ -463,9 +464,9 @@ trait NamesDefaults { self: Analyzer => else NamedArg(Ident(p.name), default2) }) } - }) + } (givenArgs ::: defaultArgs, Nil) - } else (givenArgs, missing filterNot (_.hasDefault)) + } else (givenArgs, missing.filterNot(_.hasDefault)) } else (givenArgs, Nil) } @@ -551,7 +552,7 @@ trait NamesDefaults { self: Analyzer => var positionalAllowed = true def stripNamedArg(arg: NamedArg, argIndex: Int): Tree = { val NamedArg(Ident(name), rhs) = arg: @unchecked - params indexWhere (p => matchesName(p, name, argIndex)) match { + params.indexWhere(p => matchesName(p, name, argIndex)) match { case -1 => val warnVariableInScope = !currentRun.isScala3 && context0.lookupSymbol(name, _.isVariable).isSuccess UnknownParameterNameNamesDefaultError(arg, name, warnVariableInScope) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d66c018917f8..3ae1228e47c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3735,7 +3735,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt).tap(checkRecursive) } else { rollbackNamesDefaultsOwnerChanges() - tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) + tryTupleApply orElse { + removeNames(Typer.this)(allArgs, params) // report bad names + duplErrorTree(NotEnoughArgsError(tree, fun, missing)) + } } } } diff --git a/test/files/neg/t8667.check b/test/files/neg/t8667.check index 8a0c37775990..142d7508543e 100644 --- a/test/files/neg/t8667.check +++ b/test/files/neg/t8667.check @@ -127,4 +127,20 @@ t8667.scala:76: error: can't supply unit value with infix notation because nulla t8667.scala:77: error: no arguments allowed for nullary method f0: (): Int x.f0(()) ^ -43 errors +t8667.scala:89: error: unknown parameter name: k + f2(k = 42) + ^ +t8667.scala:89: error: not enough arguments for method f2: (i: Int, j: Int): Unit. +Unspecified value parameters i, j. + f2(k = 42) + ^ +t8667.scala:90: error: unknown parameter name: k + f2(17, k = 42) + ^ +t8667.scala:91: error: unknown parameter name: k + f2(17, 27, k = 42) + ^ +t8667.scala:91: error: too many arguments (found 3, expected 2) for method f2: (i: Int, j: Int): Unit + f2(17, 27, k = 42) + ^ +48 errors diff --git a/test/files/neg/t8667.scala b/test/files/neg/t8667.scala index 09cf57041ebc..1640884e04b2 100644 --- a/test/files/neg/t8667.scala +++ b/test/files/neg/t8667.scala @@ -82,3 +82,12 @@ object app { x f0 } } + +trait Nuance { + def f2(i: Int, j: Int) = () + def `always report bad named arg`: Unit = { + f2(k = 42) + f2(17, k = 42) + f2(17, 27, k = 42) + } +} From aff45b36f851d0326b2a0b5c8957a180f3ba0067 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Thu, 5 Jan 2023 20:40:01 +0000 Subject: [PATCH 120/261] Update jquery to 3.6.3 in 2.12.x --- project/ScaladocSettings.scala | 2 +- src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/ScaladocSettings.scala b/project/ScaladocSettings.scala index c1a8698ee8c0..73d4254a4c62 100644 --- a/project/ScaladocSettings.scala +++ b/project/ScaladocSettings.scala @@ -7,7 +7,7 @@ object ScaladocSettings { // when this changes, the integrity check in HtmlFactory.scala also needs updating val webjarResources = Seq( - "org.webjars" % "jquery" % "3.6.1" + "org.webjars" % "jquery" % "3.6.3" ) def extractResourcesFromWebjar = Def.task { diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala index d76c6c311a0a..77849e07c0bf 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -95,7 +95,7 @@ class HtmlFactory(val universe: doc.Universe, val reporter: Reporter) { ) final def webjarResources = List( - ("jquery.min.js", "o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=") + ("jquery.min.js", "pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=") ) /** Generates the Scaladoc site for a model into the site root. From daa2815bae0434d41522085395d18cb357f79176 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Thu, 5 Jan 2023 20:39:53 +0000 Subject: [PATCH 121/261] Update sbt to 1.8.2 in 2.12.x --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 8b9a0b0ab037..46e43a97ed86 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.8.2 From 573d4b84ba86a51780b8eb0c45cebb8a3e116547 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Fri, 13 Jan 2023 18:39:43 +0000 Subject: [PATCH 122/261] Update scala3-compiler_3, ... to 3.2.2 --- project/DottySupport.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index a37605c08a1e..328a1ad9d782 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.2.1" // TASTy version 28.2-0 + val supportedTASTyRelease = "3.2.2" // TASTy version 28.2-0 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease From 2714f384f260da7fb9e15c7362745382144586a1 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 13 Jan 2023 14:13:24 +0100 Subject: [PATCH 123/261] Run some checks in `unit.toCheck` after typer finished all units `unit.toCheck` is used for various checks that need to be delayed, either to avoid cyclic references by forcing too much, or to wait for information to be populated (`symbol.children`). Instead of running a compilation unit's checks right when that unit is done type checking, run some checks after typer is done with all compilation units. This makes sure delayed checks see the post-typer symbol table (consistent children, no matter of compilation order). Don't delay all checks to keep messages for a unit together. Also, unused imports warnings are reported at the end of a unit, and the language flag check (in `unit.toCheck`) marks language imports as used. --- .../scala/tools/nsc/CompilationUnits.scala | 10 ++++++- .../tools/nsc/typechecker/Analyzer.scala | 11 ++++++- .../tools/nsc/typechecker/Checkable.scala | 2 +- .../scala/tools/nsc/typechecker/Namers.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 8 ++--- .../tools/nsc/typechecker/TypedTreeTest.scala | 30 +++++++++++++++++++ 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index d24ab4b6c3d4..e704a7b8e582 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -127,7 +127,9 @@ trait CompilationUnits { global: Global => val transformed = new mutable.AnyRefMap[Tree, Tree] /** things to check at end of compilation unit */ - val toCheck = new ListBuffer[() => Unit] + val toCheck = new ListBuffer[CompilationUnit.ToCheck] + private[nsc] def addPostUnitCheck(check: CompilationUnit.ToCheckAfterUnit): Unit = toCheck += check + private[nsc] def addPostTyperCheck(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheck += check /** The features that were already checked for this unit */ var checkedFeatures = Set[Symbol]() @@ -149,4 +151,10 @@ trait CompilationUnits { global: Global => override def toString() = source.toString() } + + object CompilationUnit { + sealed trait ToCheck + trait ToCheckAfterUnit extends ToCheck { def apply(): Unit } + trait ToCheckAfterTyper extends ToCheck { def apply(): Unit } + } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 80dab26b77e8..68a5d4f95eac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -13,6 +13,8 @@ package scala.tools.nsc package typechecker +import scala.collection.mutable.ListBuffer + /** Defines the sub-components for the namer, packageobjects, and typer phases. */ trait Analyzer extends AnyRef @@ -98,15 +100,19 @@ trait Analyzer extends AnyRef // Lacking a better fix, we clear it here (before the phase is created, meaning for each // compiler run). This is good enough for the resident compiler, which was the most affected. undoLog.clear() + private val toCheckAfterTyper = new ListBuffer[CompilationUnit.ToCheckAfterTyper] override def run(): Unit = { val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.typerNanos) else null global.echoPhaseSummary(this) val units = currentRun.units + while (units.hasNext) { applyPhase(units.next()) undoLog.clear() } finishComputeParamAlias() + for (work <- toCheckAfterTyper) work() + toCheckAfterTyper.clear() // defensive measure in case the bookkeeping in deferred macro expansion is buggy clearDelayed() if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.typerNanos, start) @@ -117,7 +123,10 @@ trait Analyzer extends AnyRef unit.body = typer.typed(unit.body) // interactive typed may finish by throwing a `TyperResult` if (!settings.Youtline.value) { - for (workItem <- unit.toCheck) workItem() + unit.toCheck foreach { + case now: CompilationUnit.ToCheckAfterUnit => now() + case later: CompilationUnit.ToCheckAfterTyper => toCheckAfterTyper += later + } if (!settings.isScaladoc && settings.warnUnusedImport) warnUnusedImports(unit) if (!settings.isScaladoc && settings.warnUnused.isSetByUser) diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 481531a5951d..7b792b89173b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -371,7 +371,7 @@ trait Checkable { val Xsym = X.typeSymbol val Psym = P.typeSymbol if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) - context.unit.toCheck += (() => recheckFruitless()) + context.unit.addPostTyperCheck(() => recheckFruitless()) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index ad1aee3579c4..29718f626241 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1487,7 +1487,7 @@ trait Namers extends MethodSynthesis { context.warning(ddef.pos, msg, WarningCategory.OtherNullaryOverride) meth.updateAttachment(NullaryOverrideAdapted) } - context.unit.toCheck += (if (currentRun.isScala3) error _ else warn _) + context.unit.addPostUnitCheck(() => if (currentRun.isScala3) error() else warn()) ListOfNil } else vparamSymss } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d66c018917f8..fcb9b89a1c0a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -465,7 +465,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => QualifyingClassError(tree, qual) // Delay `setError` in namer, scala/bug#10748 - if (immediate) setError(tree) else unit.toCheck += (() => setError(tree)) + if (immediate) setError(tree) else unit.addPostUnitCheck(() => setError(tree)) NoSymbol } @@ -750,7 +750,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (immediate) { action() } else { - unit.toCheck += (() => action(): Unit) + unit.addPostUnitCheck(() => action()) true } } @@ -3228,7 +3228,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper namer.enterSyms(stats) // need to delay rest of typedRefinement to avoid cyclic reference errors - unit.toCheck += { () => + unit.addPostUnitCheck(() => { val stats1 = typedStats(stats, NoSymbol) // this code kicks in only after typer, so `stats` will never be filled in time // as a result, most of compound type trees with non-empty stats will fail to reify @@ -3238,7 +3238,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper templ updateAttachment att.copy(stats = stats1) for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) stat.symbol setFlag OVERRIDE - } + }) } def typedImport(imp : Import): Import = transformed.remove(imp) match { diff --git a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala index fe798a7de36e..a90ca57a0531 100644 --- a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala +++ b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala @@ -4,10 +4,40 @@ import org.junit.Assert.{assertEquals, assertNotEquals} import org.junit.Test import scala.tools.testkit.BytecodeTesting +import scala.tools.testkit.BytecodeTesting._ class TypedTreeTest extends BytecodeTesting { override def compilerArgs = "-Ystop-after:typer" + @Test def t12703(): Unit = { + import compiler._ + val a = + """object Foo { + | sealed trait A + | sealed trait B + | final case class C(x: Int) extends A with B + |} + |""".stripMargin + val b = + """object T { + | import Foo._ + | def f(a: A) = a match { + | case b: B => 0 + | case _ => 1 + | } + |} + |""".stripMargin + + def check(a: String, b: String) = { + val r = newRun() + r.compileSources(makeSourceFile(a, "A.scala") :: makeSourceFile(b, "B.scala") :: Nil) + checkReport() + } + + check(a, b) + check(b, a) + } + @Test def keepBlockUndetparams(): Unit = { import compiler.global._ From b5041fd29c4b4dd0a188a8958e8d6e911dd2da12 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 17 Jan 2023 11:01:46 -0800 Subject: [PATCH 124/261] Clean up tempdir in test --- test/files/run/t11385.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/files/run/t11385.scala b/test/files/run/t11385.scala index a46985706f70..dd2221bdaabd 100644 --- a/test/files/run/t11385.scala +++ b/test/files/run/t11385.scala @@ -2,6 +2,8 @@ import scala.tools.partest.DirectTest import java.nio.file.Files.{createDirectories, createTempDirectory} +import scala.tools.testkit.ReleasablePath._ +import scala.util.Using // an unfortunately-named resource dir on the classpath // @@ -10,9 +12,10 @@ object Test extends DirectTest { def code = "package acme { class C }" def show() = assert { - val tmp = createTempDirectory("t11385") - val pkg = createDirectories(tmp.resolve("acme").resolve("C").resolve("sub")) - compile("-classpath", tmp.toString) + Using.resource(createTempDirectory("t11385")) { tmp => + val pkg = createDirectories(tmp.resolve("acme").resolve("C").resolve("sub")) + compile("-classpath", tmp.toString) + } } } From 05e861b547294c9150b9d7073e3b526959670d35 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 18 Jan 2023 11:11:59 +0100 Subject: [PATCH 125/261] Disallow mixins where super calls bind to vals Super calls in traits are bound when the trait is mixed into a class. Disallow mixin compositions where the target of a super call is a value (not a method). --- .../scala/tools/nsc/transform/Mixin.scala | 2 ++ test/files/neg/t12715.check | 7 ++++++ test/files/neg/t12715.scala | 23 +++++++++++++++++++ test/files/neg/t12715b.check | 4 ++++ test/files/neg/t12715b.scala | 19 +++++++++++++++ 5 files changed, 55 insertions(+) create mode 100644 test/files/neg/t12715.check create mode 100644 test/files/neg/t12715.scala create mode 100644 test/files/neg/t12715b.check create mode 100644 test/files/neg/t12715b.scala diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 98b8e0cbbad1..92a5b15b12cf 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -357,6 +357,8 @@ abstract class Mixin extends Transform with ast.TreeDSL with AccessorSynthesis { case alias1 => registerRequiredDirectInterface(alias1, clazz, parent => s"Unable to implement a super accessor required by trait ${mixinClass.name} unless $parent is directly extended by $clazz.") + if (alias1.isValue && !alias1.isMethod || alias1.isAccessor) + reporter.error(clazz.pos, s"parent $mixinClass has a super call to method ${mixinMember.alias.fullNameString}, which binds to the value ${alias1.fullNameString}. Super calls can only target methods.") superAccessor.asInstanceOf[TermSymbol] setAlias alias1 } } diff --git a/test/files/neg/t12715.check b/test/files/neg/t12715.check new file mode 100644 index 000000000000..729af9c43246 --- /dev/null +++ b/test/files/neg/t12715.check @@ -0,0 +1,7 @@ +t12715.scala:21: error: parent trait E has a super call to method B.f, which binds to the value D.f. Super calls can only target methods. +object O1 extends B with C with D with E + ^ +t12715.scala:22: error: parent trait E has a super call to method B.f, which binds to the value C.f. Super calls can only target methods. +object O2 extends B with C with E with D + ^ +2 errors diff --git a/test/files/neg/t12715.scala b/test/files/neg/t12715.scala new file mode 100644 index 000000000000..e3769141b4c4 --- /dev/null +++ b/test/files/neg/t12715.scala @@ -0,0 +1,23 @@ +trait A { + def f: String +} + +trait B extends A { + def f = "B"; +} + +trait C extends A { + override val f = "C" +} + +trait D extends C { + override val f = "D" +} + +trait E extends A with B { + def d = super.f +} + +object O1 extends B with C with D with E +object O2 extends B with C with E with D +object O3 extends B with E with C with D diff --git a/test/files/neg/t12715b.check b/test/files/neg/t12715b.check new file mode 100644 index 000000000000..3cdb24a74351 --- /dev/null +++ b/test/files/neg/t12715b.check @@ -0,0 +1,4 @@ +t12715b.scala:17: error: parent trait D has a super call to method B.f, which binds to the value C.f. Super calls can only target methods. + new A(10.0f) with C with D {} + ^ +1 error diff --git a/test/files/neg/t12715b.scala b/test/files/neg/t12715b.scala new file mode 100644 index 000000000000..9d89233dc85f --- /dev/null +++ b/test/files/neg/t12715b.scala @@ -0,0 +1,19 @@ +trait B { + def f: Float = 1.0f +} + +class A(override val f: Float) extends B + +trait C extends B { + abstract override val f = super.f + 100.0f +} + +trait D extends B { + abstract override val f = super.f + 1000.0f +} + +object Test { + def main(args: Array[String]): Unit = { + new A(10.0f) with C with D {} + } +} From 19088223c08c2407c59cd5d10d32f88c357da149 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 20 Jan 2023 22:38:13 -0800 Subject: [PATCH 126/261] Forward port minor ListSetTest tweak --- .../scala/collection/immutable/ListSetTest.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/junit/scala/collection/immutable/ListSetTest.scala b/test/junit/scala/collection/immutable/ListSetTest.scala index 4ce4fc5a6206..54acd617d46c 100644 --- a/test/junit/scala/collection/immutable/ListSetTest.scala +++ b/test/junit/scala/collection/immutable/ListSetTest.scala @@ -1,6 +1,8 @@ package scala.collection.immutable -import org.junit.Assert._ +import scala.tools.testkit.AssertUtil.{assertSameElements, fail} + +import org.junit.Assert.{assertEquals, assertSame, fail => _} import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 @@ -79,6 +81,17 @@ class ListSetTest { bar = foo ++ ListSet(1, 2, 3, 4, 5, 6) assertEquals(List(1, 2, 3, 4, 5, 6), bar.iterator.toList) + assertSameElements(List(1, 2, 3, 4, 5, 6), bar.iterator) + + assertSame(foo, foo ++ foo) + assertSame(foo, foo ++ ListSet.empty) + assertSame(foo, foo ++ Nil) + } + + @Test def `t12316 ++ is correctly ordered`: Unit = { + // was: ListSet(1, 2, 3, 42, 43, 44, 29, 28, 27, 12, 11, 10) + assertEquals(ListSet(1,2,3,42,43,44,10,11,12,27,28,29), ListSet(1,2,3,42,43,44) ++ ListSet(10,11,12,42,43,44,27,28,29)) + assertSameElements(List(1,2,3,42,43,44,10,11,12,27,28,29), ListSet(1,2,3,42,43,44) ++ ListSet(10,11,12,42,43,44,27,28,29)) } @Test From c5e9cd3fa5dd9a30866e1ed4f496cbc371be3ef6 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 21 Jan 2023 00:52:54 -0800 Subject: [PATCH 127/261] Align assertSameElements with 2.13 API --- .../scala/tools/testkit/AssertUtil.scala | 34 ++++++++++++++----- .../nsc/backend/jvm/PerRunInitTest.scala | 1 + .../scala/tools/testkit/AssertUtilTest.scala | 21 ++++++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/testkit/scala/tools/testkit/AssertUtil.scala b/src/testkit/scala/tools/testkit/AssertUtil.scala index f12bec8d2435..041cd077ab45 100644 --- a/src/testkit/scala/tools/testkit/AssertUtil.scala +++ b/src/testkit/scala/tools/testkit/AssertUtil.scala @@ -159,18 +159,34 @@ object AssertUtil { def assertFails[U](checkMessage: String => Boolean)(body: => U): Unit = assertThrows[AssertionError](body, checkMessage) - /** JUnit-style assertion for `IterableLike.sameElements`. + private def orEmpty(b: Boolean)(text: => String): String = if (b) text else "" + + /** JUnit-style assertion for `Seq#sameElements`. + * The `actual` is iterated twice if failure is reported. + */ + def assertSameElements[A, B >: A](expected: Seq[A], actual: Iterable[B], message: String = ""): Unit = + if (!expected.sameElements(actual)) + fail(f"${orEmpty(message.nonEmpty)(s"$message ")}expected:<${stringOf(expected)}> but was:<${stringOf(actual)}>") + + /** Convenience for testing iterators and non-Seqs. + * The `actual` is collected to a `List` for reporting errors. */ - def assertSameElements[A, B >: A](expected: Iterable[A], actual: Iterable[B], message: String = ""): Unit = - if (!expected.iterator.sameElements(actual)) - fail( - f"${ if (message.nonEmpty) s"$message " else "" }expected:<${ stringOf(expected) }> but was:<${ stringOf(actual) }>" - ) + def assertSameElements[A, B >: A](expected: Seq[A], actual: IterableOnce[B]): Unit = + assertSameElements(expected, actual.iterator.to(Iterable), message = "") - /** Convenient for testing iterators. + /** Convenience for testing iterators and non-Seqs. + * The `expected` is collected to a `List` for reporting errors. */ - def assertSameElements[A, B >: A](expected: Iterable[A], actual: IterableOnce[B]): Unit = - assertSameElements(expected, actual.iterator.to(List), "") + def assertSameElements[A, B >: A](expected: IterableOnce[A], actual: IterableOnce[B]): Unit = + assertSameElements(expected.iterator.to(Seq), actual) + + /** Convenience for testing arrays. Avoids warning about implicit conversion to Seq. + */ + def assertSameElements[A, B >: A](expected: Array[A], actual: Array[B]): Unit = + assertSameElements(expected, actual, message = "") + + def assertSameElements[A, B >: A](expected: Array[A], actual: Array[B], message: String): Unit = + assertSameElements(expected.toIndexedSeq, actual.toIndexedSeq, message) /** Value is not strongly reachable from roots after body is evaluated. */ diff --git a/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala b/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala index bd3b313548f8..64d7a764c327 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/PerRunInitTest.scala @@ -77,6 +77,7 @@ abstract class PerRunInitTest { def clearCaches() = underTest.global.perRunCaches.clearAll() + @annotation.nowarn("cat=deprecation") def doGc() = { System.gc() System.runFinalization() diff --git a/test/junit/scala/tools/testkit/AssertUtilTest.scala b/test/junit/scala/tools/testkit/AssertUtilTest.scala index d618ecb9ef16..9afa8d7e5618 100644 --- a/test/junit/scala/tools/testkit/AssertUtilTest.scala +++ b/test/junit/scala/tools/testkit/AssertUtilTest.scala @@ -122,4 +122,25 @@ class AssertUtilTest { assertEquals("00000000 f0 90 90 80 |𐐀.|", hexdump("\ud801\udc00").next()) } */ + + @Test def `assertSameElements reports fail of IterableOnce`: Unit = { + assertFails(_ == "expected: but was:") { + assertSameElements(List(1, 2, 3), Iterator(1, 2, 3, 4)) + } + assertFails(_ == "expected: but was:") { + assertSameElements(List(1, 2, 3), Set(1, 2, 3, 4)) + } + assertFails(_ == "expected: but was:") { + assertSameElements(Vector(1, 2, 3), Iterator(1, 2, 3, 4)) + } + assertFails(_ == "force use of canonical method expected: but was:") { + assertSameElements(Vector(1, 2, 3), Iterator(1, 2, 3, 4).to(Iterable), message = "force use of canonical method") + } + } + @Test def `assertSameElements supports Array directly`: Unit = { + assertSameElements(Array(42), Array(42)) + assertFails(_ == "expected: but was:") { + assertSameElements(Array(17), Array(42)) + } + } } From 22309050dcdb5c4f30f90d3e9aac94afcb4b8158 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 14 Jan 2023 13:06:13 -0800 Subject: [PATCH 128/261] Report total errors on stderr --- src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index 0d47fc6bb988..9dafc2426d70 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -27,9 +27,10 @@ class ConsoleReporter(val settings: Settings, val reader: BufferedReader, val wr override def finish(): Unit = { import reflect.internal.util.StringOps.countElementsAsString if (!settings.nowarn.value && hasWarnings) - echo(countElementsAsString(warningCount, WARNING.toString.toLowerCase)) + writer.println(countElementsAsString(warningCount, WARNING.toString.toLowerCase)) if (hasErrors) - echo(countElementsAsString(errorCount, ERROR.toString.toLowerCase)) + writer.println(countElementsAsString(errorCount, ERROR.toString.toLowerCase)) + writer.flush() super.finish() } } From 244c07970a6df8515f994c91b882da7379e51d8f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 21 Jan 2023 17:43:05 -0800 Subject: [PATCH 129/261] Unused diagnostics includes original of const-fold Trees munged by adaptConstant are preserved in an attachment. Traverse the original looking for unused grunge. Co-authored-by: jxnu-liguobin --- .../scala/tools/nsc/typechecker/TypeDiagnostics.scala | 2 ++ test/files/neg/t12590.check | 6 ++++++ test/files/neg/t12590.scala | 7 +++++++ test/files/run/repl-replay.check | 2 ++ 4 files changed, 17 insertions(+) create mode 100644 test/files/neg/t12590.check create mode 100644 test/files/neg/t12590.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index cc8655546ce7..1b485623e078 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -562,6 +562,8 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { case Assign(lhs, _) if isExisting(lhs.symbol) => setVars += lhs.symbol case Function(ps, _) if settings.warnUnusedParams && !t.isErrorTyped => params ++= ps.filterNot(p => atBounded(p) || p.symbol.isSynthetic).map(_.symbol) + case Literal(_) => + t.attachments.get[OriginalTreeAttachment].foreach(ota => traverse(ota.original)) case _ => } diff --git a/test/files/neg/t12590.check b/test/files/neg/t12590.check new file mode 100644 index 000000000000..c52ae56a1b44 --- /dev/null +++ b/test/files/neg/t12590.check @@ -0,0 +1,6 @@ +t12590.scala:4: warning: local val a in method unusedLocal is never used + val a = 27 + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12590.scala b/test/files/neg/t12590.scala new file mode 100644 index 000000000000..059391320087 --- /dev/null +++ b/test/files/neg/t12590.scala @@ -0,0 +1,7 @@ +// scalac: -Werror -Wunused:locals +class C { + def unusedLocal = { + val a = 27 + 42 + } +} diff --git a/test/files/run/repl-replay.check b/test/files/run/repl-replay.check index 15a46c07f82d..e8059184c308 100644 --- a/test/files/run/repl-replay.check +++ b/test/files/run/repl-replay.check @@ -6,6 +6,8 @@ scala> :replay -Xlint replay> locally { val x = 42 ; "$x" } ^ warning: possible missing interpolator: detected interpolated identifier `$x` + ^ + warning: local val x in value res0 is never used val res0: String = $x From ae5acda3756e76367cdb499151c2084297095dbd Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Mon, 23 Jan 2023 20:10:45 +0000 Subject: [PATCH 130/261] Update jackson-module-scala to 2.13.5 in 2.12.x --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 98b0d4ea2580..72c0c6ae910d 100644 --- a/build.sbt +++ b/build.sbt @@ -413,7 +413,7 @@ lazy val compilerOptionsExporter = Project("compilerOptionsExporter", file(".") .settings(disablePublishing) .settings( libraryDependencies ++= { - val jacksonVersion = "2.13.3" + val jacksonVersion = "2.13.5" Seq( "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion, From 95b6da65cc65d10f360218222af9263bc7923434 Mon Sep 17 00:00:00 2001 From: Rituraj Date: Tue, 24 Jan 2023 00:55:05 +0530 Subject: [PATCH 131/261] Leverage SAM conversion in VersionUtil --- project/VersionUtil.scala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index ef2d09009dd9..78b05f0c4685 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -7,8 +7,7 @@ import java.util.{Date, Locale, Properties, TimeZone} import java.io.{File, FileInputStream, StringWriter} import java.text.SimpleDateFormat import java.time.Instant -import java.time.format.DateTimeFormatter -import java.time.temporal.{TemporalAccessor, TemporalQueries, TemporalQuery} +import java.time.format.DateTimeFormatter.ISO_DATE_TIME import scala.collection.JavaConverters._ import BuildSettings.autoImport._ @@ -69,8 +68,8 @@ object VersionUtil { val (dateObj, sha) = { try { // Use JGit to get the commit date and SHA - import org.eclipse.jgit.storage.file.FileRepositoryBuilder import org.eclipse.jgit.revwalk.RevWalk + import org.eclipse.jgit.storage.file.FileRepositoryBuilder val db = new FileRepositoryBuilder().findGitDir.build val head = db.resolve("HEAD") if (head eq null) { @@ -79,9 +78,7 @@ object VersionUtil { // Workaround lack of git worktree support in JGit https://bugs.eclipse.org/bugs/show_bug.cgi?id=477475 val sha = List("git", "rev-parse", "HEAD").!!.trim val commitDateIso = List("git", "log", "-1", "--format=%cI", "HEAD").!!.trim - val date = java.util.Date.from(DateTimeFormatter.ISO_DATE_TIME.parse(commitDateIso, new TemporalQuery[Instant] { - override def queryFrom(temporal: TemporalAccessor): Instant = Instant.from(temporal) - })) + val date = Date.from(ISO_DATE_TIME.parse(commitDateIso, Instant.from(_))) (date, sha.substring(0, 7)) } catch { case ex: Exception => From 25aef2cd02763210f12b38d2dc299ce9c0bac265 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 24 Jan 2023 09:13:29 -0800 Subject: [PATCH 132/261] Fix test that compared ints and strings --- test/files/jvm/t7253.check | 1 - test/files/jvm/t7253/test.scala | 33 ++++++++++++--------------------- 2 files changed, 12 insertions(+), 22 deletions(-) delete mode 100644 test/files/jvm/t7253.check diff --git a/test/files/jvm/t7253.check b/test/files/jvm/t7253.check deleted file mode 100644 index 43f53aba123d..000000000000 --- a/test/files/jvm/t7253.check +++ /dev/null @@ -1 +0,0 @@ -bytecode identical diff --git a/test/files/jvm/t7253/test.scala b/test/files/jvm/t7253/test.scala index bb51650e8a02..c506a116ae0c 100644 --- a/test/files/jvm/t7253/test.scala +++ b/test/files/jvm/t7253/test.scala @@ -1,29 +1,20 @@ -import scala.tools.partest.BytecodeTest -import scala.tools.testkit.ASMConverters +// scalac: -Werror -Xlint -import scala.tools.nsc.util.JavaClassPath -import java.io.InputStream -import scala.tools.asm -import asm.ClassReader -import asm.tree.{ClassNode, InsnList} -import scala.collection.JavaConverters._ +import scala.tools.asm.Opcodes +import scala.tools.partest.BytecodeTest +import scala.tools.testkit.ASMConverters._ object Test extends BytecodeTest { - import ASMConverters._ - def show: Unit = { - val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructionsFromMethod(getMethod(loadClassNode(name), "foo"))) - val instrSeqs = instrBaseSeqs map (_ filter isInvoke) - cmpInstructions(instrSeqs(0), instrSeqs(1)) + def show(): Unit = { + def fooOf(name: String) = instructionsFromMethod(getMethod(loadClassNode(name), "foo")).filter(isInvoke) + cmpInstructions(fooOf("ScalaClient_1"), fooOf("JavaClient_1")) } - def cmpInstructions(isa: List[Instruction], isb: List[Instruction]) = { - if (isa == isb) println("bytecode identical") - else diffInstructions(isa, isb) - } + def cmpInstructions(isa: List[Instruction], isb: List[Instruction]) = + if (!isa.sameElements(isb)) + diffInstructions(isa, isb) - def isInvoke(node: Instruction): Boolean = { - val opcode = node.opcode - (opcode == "INVOKEVIRTUAL") || (opcode == "INVOKEINTERFACE") - } + def isInvoke(node: Instruction): Boolean = + node.opcode == Opcodes.INVOKEVIRTUAL || node.opcode == Opcodes.INVOKEINTERFACE } From 37392e54fd9aa7ab866c7835dca62b143ce891aa Mon Sep 17 00:00:00 2001 From: ashish Date: Wed, 25 Jan 2023 13:45:30 +0530 Subject: [PATCH 133/261] codeOptimisation --- project/DottySupport.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index 328a1ad9d782..5113d6d950fa 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -41,7 +41,7 @@ object DottySupport { // Add the scala3-library sources to the sourcepath and disable fatal warnings Compile / scalacOptions := { val old = (Compile / scalacOptions).value - val withoutFatalWarnings = old.filter(opt => opt != "-Werror" && !opt.startsWith("-Wconf")) + val withoutFatalWarnings = old.filterNot(opt => opt == "-Werror" || opt.startsWith("-Wconf")) val (beforeSourcepath, "-sourcepath" :: oldSourcepath :: afterSourcePath) = withoutFatalWarnings.span(_ != "-sourcepath") From deb5a5f11555387cae122c9ff127de450d15eae4 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 25 Jan 2023 06:15:35 -0800 Subject: [PATCH 134/261] Partest --branch checks file status --- .../scala/tools/partest/nest/AbstractRunner.scala | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index 829c447f8a85..31d62dd854b9 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -245,12 +245,24 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour case parseVerbose(tracking) => tracking.tap(ref => echo(s"Tracking $ref")) case _ => "upstream/2.13.x".tap(default => echoWarning(s"Tracking default $default, failed to understand '$line'")) } + case class MADFile(path: String, status: Int) + // D test/files/neg/t12590.scala + def madden(line: String): MADFile = + line.split("\\s+") match { + case Array(mad, p) => + val score = mad match { case "M" => 0 case "A" => 1 case "D" => -1 } + MADFile(p, score) + case _ => + echoWarning(s"diff --name-status, failed to understand '$line'") + MADFile("NOPATH", -1) + } + def isPresent(mad: MADFile) = mad.status >= 0 def isTestFiles(path: String) = path.startsWith("test/files/") val maybeFiles = for { current <- runGit("rev-parse --abbrev-ref HEAD")(_.head).tap(_.foreach(b => echo(s"Testing on branch $b"))) tracking <- runGit(s"branch -vv --list $current")(lines => parseTracking(lines.head)) - files <- runGit(s"diff --name-only $tracking")(lines => lines.filter(isTestFiles).toList) + files <- runGit(s"diff --name-status $tracking")(lines => lines.map(madden).filter(isPresent).map(_.path).filter(isTestFiles).toList) } yield files //test/files/neg/t12349.check From e5145e05468a8a62c1db068b20119d7d7db0f0fd Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 26 Jan 2023 11:59:47 -0800 Subject: [PATCH 135/261] Avoid deprecation in run/analyzerPlugins --- test/files/run/analyzerPlugins.check | 1 - test/files/run/analyzerPlugins.scala | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index 2659fd3b3e6e..7338a468ff1f 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -1,4 +1,3 @@ -warning: 1 deprecation (since 2.13.0); re-run with -deprecation for details adaptBoundsToAnnots(List( <: Int), List(type T), List(Int @testAnn)) [2] annotationsConform(Boolean @testAnn, Boolean @testAnn) [2] annotationsConform(Boolean @testAnn, Boolean) [1] diff --git a/test/files/run/analyzerPlugins.scala b/test/files/run/analyzerPlugins.scala index 81b085d74fcb..e5720649b7db 100644 --- a/test/files/run/analyzerPlugins.scala +++ b/test/files/run/analyzerPlugins.scala @@ -2,6 +2,7 @@ import scala.tools.partest._ import scala.tools.nsc._ object Test extends DirectTest { + override def extraSettings: String = s"${super.extraSettings} '-Wconf:cat=deprecation&msg=early initializers:s'" def code = """ class testAnn extends annotation.TypeConstraint From 3c41e6ee1113509f23163ea6fa9ac96fded05468 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 27 Jan 2023 10:05:35 +0100 Subject: [PATCH 136/261] [backport] flag eta-expanding methods without parameter lists under source:2.13 --- .../scala/tools/nsc/typechecker/ContextErrors.scala | 12 ++++++++++++ .../scala/tools/nsc/typechecker/Typers.scala | 10 +++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 700f154a4bfd..45c130dd0a34 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -417,6 +417,18 @@ trait ContextErrors { } //typedEta + private def mkUnderscoreNullaryEtaMessage(what: String) = + s"Methods without a parameter list and by-name params can $what be converted to functions as `m _`, " + + "write a function literal `() => m` instead" + + final val UnderscoreNullaryEtaWarnMsg = mkUnderscoreNullaryEtaMessage("no longer") + final val UnderscoreNullaryEtaErrorMsg = mkUnderscoreNullaryEtaMessage("not") + + def UnderscoreNullaryEtaError(tree: Tree) = { + issueNormalTypeError(tree, UnderscoreNullaryEtaErrorMsg) + setError(tree) + } + def UnderscoreEtaError(tree: Tree) = { issueNormalTypeError(tree, "_ must follow method; cannot follow " + tree.tpe) setError(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ae5568daa5a0..cc320a1f904a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4736,7 +4736,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val funSym = context.owner.newAnonymousFunctionValue(pos) new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue - typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt) + val result = typed(Function(Nil, methodValue) setSymbol funSym setPos pos, mode, pt) + + if (currentRun.isScala3) { + UnderscoreNullaryEtaError(methodValue) + } else { + if (currentRun.isScala213) + context.deprecationWarning(pos, NoSymbol, UnderscoreNullaryEtaWarnMsg, "2.13.2") + result + } case ErrorType => methodValue From bb82366cd624e7466e0918885f898156760b6d24 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 11 Jan 2023 12:50:13 -0800 Subject: [PATCH 137/261] Improve 'did you mean?' suggestions Backport dotty behavior of ranking results. Improve by offering a few alternatives, with subtle wording to differentiate first rank from the rest. Allow shorter matches if it looks like the typo is char swaps and repetitions. --- .../tools/nsc/typechecker/ContextErrors.scala | 62 +++++++++++------- .../scala/tools/nsc/util/StringUtil.scala | 9 +-- test/files/neg/suggest-similar.check | 63 ++++++++++++++++++- test/files/neg/suggest-similar.scala | 57 ++++++++++++++++- test/files/neg/t11843.check | 8 +-- test/files/neg/t3346c.check | 1 - test/files/neg/t4271.check | 3 +- test/files/neg/t5801.check | 1 - test/files/neg/t7475f.check | 1 + .../macro-typecheck-implicitsdisabled.check | 2 +- .../files/run/repl-no-imports-no-predef.check | 2 +- .../toolbox_typecheck_implicitsdisabled.check | 2 +- test/tasty/neg/src-2/TestInvisibleDefs.check | 1 + 13 files changed, 170 insertions(+), 42 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 29630cd2cb15..09b2bb644d22 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -407,50 +407,70 @@ trait ContextErrors extends splain.SplainErrors { //typedSelect def NotAMemberError(sel: Tree, qual: Tree, name: Name, cx: Context) = { - import util.{ EditDistance, StringUtil } + import util.EditDistance, util.StringUtil.oxford def errMsg: String = { + val editThreshold = 3 + val maxSuggestions = 4 + val owner = qual.tpe.typeSymbol val target = qual.tpe.widen def targetKindString = if (owner.isTypeParameterOrSkolem) "type parameter " else "" def nameString = decodeWithKind(name, owner) /* Illuminating some common situations and errors a bit further. */ def addendum = { - def orEmpty(cond: Boolean)(s: => String) = if (cond) s else "" + @inline def orEmpty(cond: Boolean)(s: => String) = if (cond) s else "" val companionSymbol: Symbol = { if (name.isTermName && owner.isPackageClass) target.member(name.toTypeName) else NoSymbol } val companion = orEmpty(companionSymbol != NoSymbol)(s"note: $companionSymbol exists, but it has no companion object.") - // find out all the names available under target within 2 edit distances - lazy val alternatives: List[String] = { - val editThreshold = 2 + // find out all the names available under target within ~2 edit distances + lazy val alternatives: List[(Int, String)] = { val x = name.decode - if (context.openImplicits.nonEmpty || (x.size < 2) || x.endsWith("=")) Nil + // effectively suppress comparison ops, but if they say <= and there is >=, offer it + def isEncodedComparison(n: Name) = n match { + case nme.EQ | nme.NE | nme.LT | nme.GT | nme.LE | nme.GE => true + case _ => false + } + val nameIsComparison = isEncodedComparison(name) + if (context.openImplicits.nonEmpty || x.length < 2) Nil else { target.members.iterator - .filter(sym => (sym.isTerm == name.isTermName) && + .filter(sym => sym.isTerm == name.isTermName && !sym.isConstructor && !nme.isLocalName(sym.name) && + isEncodedComparison(sym.name) == nameIsComparison && + sym.name != nme.EQ && sym.name != nme.NE && cx.isAccessible(sym, target)) .map(_.name.decode) - .filter(n => (n.length > 2) && - (math.abs(n.length - x.length) <= editThreshold) && - (n != x) && - !n.contains("$") && - EditDistance.levenshtein(n, x) <= editThreshold) - .distinct.toList + .filter { n => + math.abs(n.length - x.length) <= editThreshold && + n != x && + !n.contains("$") + } + .map(n => (EditDistance.levenshtein(n, x), n)) + .filter { case (d, n) => + val nset = n.endsWith("_=") + val xset = x.endsWith("_=") + val (n1, x1) = if (nset && xset) (n.dropRight(2), x.dropRight(2)) else (n, x) + def contained = x1.forall(c => n1.indexOf(c) >= 0) + !(nset ^ xset) && d <= editThreshold && (d <= n1.length/2 && d <= x1.length/2 || contained) + } + .toList.sorted } } - val altStr: String = { - val maxSuggestions = 4 - orEmpty(companionSymbol == NoSymbol) { - alternatives match { - case Nil => "" - case xs => s"did you mean ${StringUtil.oxford(xs.sorted.take(maxSuggestions), "or")}?" - } + val altStr: String = + orEmpty(companionSymbol == NoSymbol && alternatives.nonEmpty) { + val d0 = alternatives.head._1 + val (best0, rest0) = alternatives.span(_._1 == d0) + val best = best0.map(_._2).distinct + val rest = rest0.map(_._2).distinct + val more = (maxSuggestions - best.length) max 0 + val add1 = orEmpty(more > 0 && rest.nonEmpty)(s" or perhaps ${oxford(rest.take(more), "or")}?") + val add2 = orEmpty(best.length > maxSuggestions || rest.length > more)(" or...?") + s"did you mean ${oxford(best.take(maxSuggestions), "or")}?$add1$add2" } - } val semicolon = orEmpty(linePrecedes(qual, sel))(s"possible cause: maybe a semicolon is missing before `$nameString`?") val notAnyRef = orEmpty(ObjectClass.info.member(name).exists)(notAnyRefMessage(target)) val javaRules = orEmpty(owner.isJavaDefined && owner.isClass && !owner.hasPackageFlag) { diff --git a/src/compiler/scala/tools/nsc/util/StringUtil.scala b/src/compiler/scala/tools/nsc/util/StringUtil.scala index 14eb7aae381b..cbe98088a26e 100644 --- a/src/compiler/scala/tools/nsc/util/StringUtil.scala +++ b/src/compiler/scala/tools/nsc/util/StringUtil.scala @@ -15,15 +15,12 @@ package util import scala.collection.immutable.Seq -trait StringUtil { - def oxford(vs: Seq[String], conj: String): String = { +object StringUtil { + def oxford(vs: Seq[String], conj: String): String = vs match { case Seq() => "" case Seq(a) => a case Seq(a, b) => s"$a $conj $b" - case xs => xs.dropRight(1).mkString(", ") + s", $conj " + xs.last + case xs => xs.init.mkString("", ", ", s", $conj ${xs.last}") } - } } - -object StringUtil extends StringUtil diff --git a/test/files/neg/suggest-similar.check b/test/files/neg/suggest-similar.check index 66b01a71cc4f..1eac32d47248 100644 --- a/test/files/neg/suggest-similar.check +++ b/test/files/neg/suggest-similar.check @@ -10,7 +10,7 @@ did you mean Weehawken? new example.Eeehawken ^ suggest-similar.scala:16: error: value readline is not a member of object scala.io.StdIn -did you mean readLine? +did you mean readLine? or perhaps readByte, readInt, or readLong? import scala.io.StdIn.{readline, readInt} ^ suggest-similar.scala:20: error: object stdin is not a member of package io @@ -18,11 +18,68 @@ did you mean StdIn? import scala.io.stdin.{readLine => line} ^ suggest-similar.scala:37: error: value foo is not a member of object example.Hohokus -did you mean foo1, foo2, foo3, or foo4? +did you mean foo1, foo2, foo3, or foo4? or...? Hohokus.foo ^ suggest-similar.scala:41: error: value bar is not a member of example.Hohokus did you mean bar2? new Hohokus().bar // don't suggest bar1 ^ -7 errors +suggest-similar.scala:54: error: value acb is not a member of example.assignments.C +did you mean abc? + def f = c.acb(42) + ^ +suggest-similar.scala:55: error: value ++- is not a member of example.assignments.C +did you mean +++? + def g = c.++-(42) + ^ +suggest-similar.scala:56: error: value ++= is not a member of example.assignments.C +did you mean +++? + def h = c.++=(42) + ^ +suggest-similar.scala:57: error: value ++= is not a member of example.assignments.C +did you mean +++? + Expression does not convert to assignment because receiver is not assignable. + def i = c ++= 42 + ^ +suggest-similar.scala:59: error: value y_= is not a member of example.assignments.C + def v = c y_= 1 + ^ +suggest-similar.scala:60: error: value y is not a member of example.assignments.C + def v2 = c.y = 1 + ^ +suggest-similar.scala:61: error: value x_== is not a member of example.assignments.C + def w = c x_== 1 + ^ +suggest-similar.scala:62: error: value xx_= is not a member of example.assignments.C +did you mean x_=? + def y = c.xx_=(1) + ^ +suggest-similar.scala:63: error: reassignment to val + def y2 = c.xx = 1 + ^ +suggest-similar.scala:65: error: value zzz_= is not a member of example.assignments.C +did you mean zz_=? or perhaps z_=? + def z2 = c.zzz_=(1) + ^ +suggest-similar.scala:66: error: value ++++ is not a member of example.assignments.C +did you mean +++? + def z3 = c.++++(1) + ^ +suggest-similar.scala:67: error: value xxx is not a member of example.assignments.C +did you mean xx? or perhaps x? + def legacy_duple = c.xxx // did not suggest c.xx as too short + ^ +suggest-similar.scala:76: error: value missN is not a member of example.MaxAlt +did you mean miss0, miss1, miss2, or miss3? or...? + def test: Int = new MaxAlt().missN + ^ +suggest-similar.scala:83: error: value missN is not a member of example.MissAlt +did you mean miss0, miss1, or miss2? + def test: Int = new MissAlt().missN + ^ +suggest-similar.scala:94: error: value missN is not a member of example.MoreAlt +did you mean miss0, miss1, miss2, or miss3? or...? + def test: Int = new MoreAlt().missN + ^ +22 errors diff --git a/test/files/neg/suggest-similar.scala b/test/files/neg/suggest-similar.scala index 92dc0b826e41..f10b2d927be7 100644 --- a/test/files/neg/suggest-similar.scala +++ b/test/files/neg/suggest-similar.scala @@ -21,8 +21,8 @@ object C { } class Hohokus { - protected def bar1 - protected[example] def bar2 + protected def bar1: Unit = () + protected[example] def bar2: Unit = () } object Hohokus { def foo1 = 1 @@ -40,3 +40,56 @@ object D { object E { new Hohokus().bar // don't suggest bar1 } + +object assignments { + class C { + def abc(i: Int) = i + def +++(i: Int) = i + var x = 42 + val xx = 42 + var z = 42 + var zz = 42 + } + val c = new C + def f = c.acb(42) + def g = c.++-(42) + def h = c.++=(42) + def i = c ++= 42 + def u = c x_= 1 + def v = c y_= 1 + def v2 = c.y = 1 + def w = c x_== 1 + def y = c.xx_=(1) + def y2 = c.xx = 1 + def z = c.zz_=(1) + def z2 = c.zzz_=(1) + def z3 = c.++++(1) + def legacy_duple = c.xxx // did not suggest c.xx as too short +} + +class MaxAlt { + def miss0 = 42 + def miss1 = 42 + def miss2 = 42 + def miss3 = 42 + def miss33 = 42 + def test: Int = new MaxAlt().missN +} + +class MissAlt { + def miss0 = 42 + def miss1 = 42 + def miss2 = 42 + def test: Int = new MissAlt().missN +} + +class MoreAlt { + def miss0 = 42 + def miss1 = 42 + def miss2 = 42 + def miss3 = 42 + def miss4 = 42 + def miss5 = 42 + def miss6 = 42 + def test: Int = new MoreAlt().missN +} diff --git a/test/files/neg/t11843.check b/test/files/neg/t11843.check index 09f56848e53d..35a071d8841e 100644 --- a/test/files/neg/t11843.check +++ b/test/files/neg/t11843.check @@ -1,17 +1,17 @@ t11843.scala:6: error: value $isInstanceOf is not a member of String -did you mean asInstanceOf or isInstanceOf? +did you mean isInstanceOf? or perhaps asInstanceOf? "".$isInstanceOf[Int] ^ t11843.scala:7: error: value $asInstanceOf is not a member of String -did you mean asInstanceOf or isInstanceOf? +did you mean asInstanceOf? or perhaps isInstanceOf? "".$asInstanceOf[Int] ^ t11843.scala:10: error: value $isInstanceOf is not a member of Symbol -did you mean asInstanceOf or isInstanceOf? +did you mean isInstanceOf? or perhaps asInstanceOf? ss.$isInstanceOf[String] ^ t11843.scala:11: error: value $asInstanceOf is not a member of Symbol -did you mean asInstanceOf or isInstanceOf? +did you mean asInstanceOf? or perhaps isInstanceOf? ss.$asInstanceOf[String] ^ t11843.scala:8: warning: fruitless type test: a value of type Symbol cannot also be a String (the underlying of String) diff --git a/test/files/neg/t3346c.check b/test/files/neg/t3346c.check index 4b18680e493d..7ffdda688f76 100644 --- a/test/files/neg/t3346c.check +++ b/test/files/neg/t3346c.check @@ -1,5 +1,4 @@ t3346c.scala:65: error: value bar is not a member of Either[Int,String] -did you mean map? eii.bar ^ 1 error diff --git a/test/files/neg/t4271.check b/test/files/neg/t4271.check index d89d07f420a7..f4f3c9438a20 100644 --- a/test/files/neg/t4271.check +++ b/test/files/neg/t4271.check @@ -1,11 +1,12 @@ t4271.scala:9: error: value to is not a member of Int +did you mean toInt? 3 to 5 ^ t4271.scala:10: error: value ensuring is not a member of Int 5 ensuring true ^ t4271.scala:11: error: value -> is not a member of Int -did you mean >>>? +did you mean >>? 3 -> 5 ^ t4271.scala:3: warning: Implicit definition should have explicit type (inferred foo.Donotuseme.type) diff --git a/test/files/neg/t5801.check b/test/files/neg/t5801.check index 7f6cb4cfe6c3..f1e672c02e46 100644 --- a/test/files/neg/t5801.check +++ b/test/files/neg/t5801.check @@ -1,5 +1,4 @@ t5801.scala:1: error: object sth is not a member of package scala -did you mean math or sys? import scala.sth ^ t5801.scala:4: error: not found: value sth diff --git a/test/files/neg/t7475f.check b/test/files/neg/t7475f.check index 4f69d9a63fd1..ad16bb89e564 100644 --- a/test/files/neg/t7475f.check +++ b/test/files/neg/t7475f.check @@ -5,6 +5,7 @@ t7475f.scala:13: error: not found: value c2 c2 // a member, but inaccessible. ^ t7475f.scala:26: error: value d2 is not a member of D[Any] +did you mean d1? other.d2 // not a member ^ 3 errors diff --git a/test/files/run/macro-typecheck-implicitsdisabled.check b/test/files/run/macro-typecheck-implicitsdisabled.check index d5f0afc71706..b937087183e4 100644 --- a/test/files/run/macro-typecheck-implicitsdisabled.check +++ b/test/files/run/macro-typecheck-implicitsdisabled.check @@ -1,3 +1,3 @@ scala.Predef.ArrowAssoc[Int](1).->[Int](2) scala.reflect.macros.TypecheckException: value -> is not a member of Int -did you mean >>>? +did you mean >>? diff --git a/test/files/run/repl-no-imports-no-predef.check b/test/files/run/repl-no-imports-no-predef.check index 380a21a41ff7..4dc6658ecf54 100644 --- a/test/files/run/repl-no-imports-no-predef.check +++ b/test/files/run/repl-no-imports-no-predef.check @@ -21,7 +21,7 @@ val res5: (Int, Int) = (1,2) scala> 1 -> 2 ^ error: value -> is not a member of Int - did you mean >>>? + did you mean >>? scala> diff --git a/test/files/run/toolbox_typecheck_implicitsdisabled.check b/test/files/run/toolbox_typecheck_implicitsdisabled.check index 6aeebb1b2bd7..35d0300e2f71 100644 --- a/test/files/run/toolbox_typecheck_implicitsdisabled.check +++ b/test/files/run/toolbox_typecheck_implicitsdisabled.check @@ -3,4 +3,4 @@ scala.Predef.ArrowAssoc[Int](1).->[Int](2) } scala.tools.reflect.ToolBoxError: reflective typecheck has failed: value -> is not a member of Int -did you mean >>>? +did you mean >>? diff --git a/test/tasty/neg/src-2/TestInvisibleDefs.check b/test/tasty/neg/src-2/TestInvisibleDefs.check index 9ce3bf4804cd..c1c95601ec33 100644 --- a/test/tasty/neg/src-2/TestInvisibleDefs.check +++ b/test/tasty/neg/src-2/TestInvisibleDefs.check @@ -8,6 +8,7 @@ TestInvisibleDefs_fail.scala:11: error: value getStatus is not a member of tasty mybean.getStatus() // error ^ TestInvisibleDefs_fail.scala:12: error: value setStatus is not a member of tastytest.InvisibleDefs.MyBean +did you mean status? mybean.setStatus("closed") // error ^ 4 errors From 60a7e9c06ecd654925ab162fdd0bc840b896d4dc Mon Sep 17 00:00:00 2001 From: Georgi Chochov Date: Mon, 30 Jan 2023 13:57:29 +0100 Subject: [PATCH 138/261] Fix typo, traitss -> traits --- src/library/scala/AnyVal.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/scala/AnyVal.scala b/src/library/scala/AnyVal.scala index 1935a82c1b7f..018c388e9228 100644 --- a/src/library/scala/AnyVal.scala +++ b/src/library/scala/AnyVal.scala @@ -38,7 +38,7 @@ package scala * User-defined value classes which avoid object allocation... * * - must have a single `val` parameter that is the underlying runtime representation. - * - can define `def`s, but no `val`s, `var`s, or nested `traits`s, `class`es or `object`s. + * - can define `def`s, but no `val`s, `var`s, or nested `trait`s, `class`es or `object`s. * - typically extend no other trait apart from `AnyVal`. * - cannot be used in type tests or pattern matching. * - may not override `equals` or `hashCode` methods. From ec07b49f92fc32d473eb97aef2b109ff94d3ba14 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 19 Jan 2023 15:26:52 +0000 Subject: [PATCH 139/261] Patmat: Fix over-prunning of private children --- .../nsc/transform/patmat/MatchAnalysis.scala | 7 +++++-- test/files/neg/t6159.check | 7 +++++++ test/files/neg/t6159.scala | 13 +++++++++++++ test/files/pos/t12712.scala | 16 ++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t6159.check create mode 100644 test/files/neg/t6159.scala create mode 100644 test/files/pos/t12712.scala diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 3b5b05b2bdba..8c746cc78494 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -129,8 +129,11 @@ trait TreeAndTypeAnalysis extends Debugging { def filterAndSortChildren(children: Set[Symbol]) = { // symbols which are both sealed and abstract need not be covered themselves, // because all of their children must be and they cannot otherwise be created. - children.toList.filter(x => !(x.isSealed || x.isPrivate) || !x.isAbstractClass || isPrimitiveValueClass(x)) - .sortBy(_.sealedSortName) + val children1 = children.toList.filterNot(child => child.isSealed && child.isAbstractClass).sortBy(_.sealedSortName) + children1.filterNot { child => + // remove private abstract children that are superclasses of other children, for example in t6159 drop X2 + child.isPrivate && child.isAbstractClass && children1.exists(sym => (sym ne child) && sym.isSubClass(child)) + } } @tailrec def groupChildren(wl: List[Symbol], acc: List[List[Symbol]]): List[List[Symbol]] = wl match { diff --git a/test/files/neg/t6159.check b/test/files/neg/t6159.check new file mode 100644 index 000000000000..19da9e4aa0cc --- /dev/null +++ b/test/files/neg/t6159.check @@ -0,0 +1,7 @@ +t6159.scala:10: warning: match may not be exhaustive. +It would fail on the following input: (_ : A.this.X2) + def f(x: X) = x match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t6159.scala b/test/files/neg/t6159.scala new file mode 100644 index 000000000000..2e7477a299c7 --- /dev/null +++ b/test/files/neg/t6159.scala @@ -0,0 +1,13 @@ +// scalac: -Werror +// like test/files/pos/t6159.scala +// but with T2 not private +trait A { + sealed abstract class X + private class X1 extends X with X2 { } + trait X2 extends X + sealed trait X3 extends X + + def f(x: X) = x match { + case _: X1 => 0 + } +} diff --git a/test/files/pos/t12712.scala b/test/files/pos/t12712.scala new file mode 100644 index 000000000000..195743b46770 --- /dev/null +++ b/test/files/pos/t12712.scala @@ -0,0 +1,16 @@ +// scalac: -Werror +object T { + private sealed trait T + private object O extends T + private trait U extends T + private object P extends U + + private def t(t: T) = t match { + case O => () + case _: U => println("hai") + } + + def main(args: Array[String]): Unit = { + t(P) + } +} From 0ca2fb37ccda4a4d5981c78f2add85aa12f8b1ea Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 26 Jan 2023 12:14:41 -0800 Subject: [PATCH 140/261] Unused warning shows uniqid The edge cases are how to name setters, and for some reason parameter was using toString, so now instead of `parameter value p` it says simply `parameter p`. The test must obfuscate the uniqid to accommodate different platforms and runs. --- .../nsc/typechecker/TypeDiagnostics.scala | 19 +++++---- .../tools/partest/nest/AbstractRunner.scala | 8 ++-- .../scala/tools/partest/nest/Runner.scala | 12 ++++-- test/files/neg/macro-annot-unused-param.check | 2 +- test/files/neg/patmat-exprs-b.check | 6 +-- test/files/neg/t10790.check | 2 +- test/files/neg/t12720.check | 40 +++++++++++++++++++ test/files/neg/t12720.scala | 32 +++++++++++++++ test/files/neg/warn-unused-explicits.check | 2 +- test/files/neg/warn-unused-implicits.check | 4 +- test/files/neg/warn-unused-params.check | 22 +++++----- 11 files changed, 115 insertions(+), 34 deletions(-) create mode 100644 test/files/neg/t12720.check create mode 100644 test/files/neg/t12720.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 1b485623e078..960741741f73 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -706,15 +706,18 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { ) val why = if (sym.isPrivate) "private" else "local" var cond = "is never used" + def long = if (settings.uniqid.value) s" (${sym.nameString})" else "" + def getterNameString(sym: Symbol): String = sym.getterName.decoded + long val what = if (sym.isDefaultGetter) "default argument" else if (sym.isConstructor) "constructor" - else if (sym.isSetter) { cond = valAdvice ; s"var ${sym.getterName.decoded}" } - else if (sym.isVar || sym.isGetter && sym.accessed.isVar) s"var ${sym.getterName.decoded}" - else if (sym.isVal || sym.isGetter && sym.accessed.isVal || sym.isLazy) s"val ${sym.name.decoded}" - else if (sym.isMethod) s"method ${sym.name.decoded}" - else if (sym.isModule) s"object ${sym.name.decoded}" + else if (sym.isSetter) { cond = valAdvice ; s"var ${getterNameString(sym)}" } + else if (sym.isVar || sym.isGetter && sym.accessed.isVar) s"var ${sym.nameString}" + else if (sym.isVal || sym.isGetter && sym.accessed.isVal || sym.isLazy) s"val ${sym.nameString}" + else if (sym.isMethod) s"method ${sym.nameString}" + else if (sym.isModule) s"object ${sym.nameString}" else "term" + // consider using sym.owner.fullLocationString emitUnusedWarning(pos, s"$why $what in ${sym.owner} $cond", wcat(sym), sym) } def typeWarning(defn: SymTree): Unit = { @@ -726,7 +729,7 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { for (defn <- unusedPrivates.unusedTypes if shouldWarnOn(defn.symbol)) { typeWarning(defn) } for (v <- unusedPrivates.unsetVars) { - emitUnusedWarning(v.pos, s"local var ${v.name} in ${v.owner} ${valAdvice}", WarningCategory.UnusedPrivates, v) + emitUnusedWarning(v.pos, s"local var ${v.nameString} in ${v.owner} ${valAdvice}", WarningCategory.UnusedPrivates, v) } } if (settings.warnUnusedPatVars) { @@ -756,8 +759,8 @@ trait TypeDiagnostics extends splain.SplainDiagnostics { ) for (s <- unusedPrivates.unusedParams if warnable(s)) { val what = - if (s.name.startsWith(nme.EVIDENCE_PARAM_PREFIX)) s"evidence parameter ${s.name} of type ${s.tpe}" - else s"parameter ${s/*.name*/}" + if (s.name.startsWith(nme.EVIDENCE_PARAM_PREFIX)) s"evidence parameter ${s.nameString} of type ${s.tpe}" + else s"parameter ${s.nameString}" val where = if (s.owner.isAnonymousFunction) "anonymous function" else s.owner emitUnusedWarning(s.pos, s"$what in $where is never used", WarningCategory.UnusedParams, s) diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index 31d62dd854b9..8a19b6e893a0 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -188,12 +188,12 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour echo(message) levyJudgment() } - if (realeasy) { - runGit("status --porcelain")(_.filter(_.endsWith(".check")).map(_.drop(3)).mkString("\n")).foreach { danger => + if (realeasy) + for (lines <- runGit("status --porcelain")(_.filter(_.endsWith(".check")).map(_.drop(3))) if lines.nonEmpty) { echo(bold(red("# There are uncommitted check files!"))) - echo(s"$danger\n") + for (file <- lines) + echo(s"$file\n") } - } } } diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 17e672203190..43041210c40d 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -366,12 +366,18 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // no spaces in test file paths below root, because otherwise how to detect end of path string? val pathFinder = raw"""(?i)\Q${elided}${File.separator}\E([\${File.separator}\S]*)""".r - def canonicalize(s: String): String = - pathFinder.replaceAllIn(s, m => Regex.quoteReplacement(ellipsis + squashSlashes(m group 1))) + def canonicalize: String => String = { + val hiders = toolArgs("hide", split = false).map(_.r) + (s: String) => { + val pathless = pathFinder.replaceAllIn(s, m => Regex.quoteReplacement(ellipsis + squashSlashes(m.group(1)))) + if (hiders.isEmpty) pathless + else hiders.foldLeft(pathless)((s, r) => r.replaceAllIn(s, m => "***")) + } + } def masters = { val files = List(new File(parentFile, "filters"), new File(suiteRunner.pathSettings.srcDir.path, "filters")) - files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filter(s => !(s startsWith "#")) + files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filterNot(_.startsWith("#")) } val filters = toolArgs("filter", split = false) ++ masters val elisions = ListBuffer[String]() diff --git a/test/files/neg/macro-annot-unused-param.check b/test/files/neg/macro-annot-unused-param.check index f23c11962c34..046e938dfe13 100644 --- a/test/files/neg/macro-annot-unused-param.check +++ b/test/files/neg/macro-annot-unused-param.check @@ -1,4 +1,4 @@ -Test_2.scala:2: warning: parameter value x in anonymous function is never used +Test_2.scala:2: warning: parameter x in anonymous function is never used @mymacro ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/patmat-exprs-b.check b/test/files/neg/patmat-exprs-b.check index c1a39e7f5565..40003f76ead4 100644 --- a/test/files/neg/patmat-exprs-b.check +++ b/test/files/neg/patmat-exprs-b.check @@ -1,10 +1,10 @@ -patmat-exprs-b.scala:42: warning: parameter value num in class Add is never used +patmat-exprs-b.scala:42: warning: parameter num in class Add is never used case class Add[T](args: Iterable[Expr[T]])(implicit @nowarn num: NumericOps[T]) extends ManyArg[T] { ^ -patmat-exprs-b.scala:46: warning: parameter value num in class Add2 is never used +patmat-exprs-b.scala:46: warning: parameter num in class Add2 is never used case class Add2[T](left: Expr[T], right: Expr[T])(implicit @nowarn num: NumericOps[T]) extends TwoArg[T] { ^ -patmat-exprs-b.scala:49: warning: parameter value num in class Add3 is never used +patmat-exprs-b.scala:49: warning: parameter num in class Add3 is never used case class Add3[T](a1: Expr[T], a2: Expr[T], a3: Expr[T])(implicit @nowarn num: NumericOps[T]) extends ManyArg[T] { ^ patmat-exprs-b.scala:42: warning: @nowarn annotation does not suppress any warnings diff --git a/test/files/neg/t10790.check b/test/files/neg/t10790.check index 77c54d47926f..2facf5713ed5 100644 --- a/test/files/neg/t10790.check +++ b/test/files/neg/t10790.check @@ -4,7 +4,7 @@ t10790.scala:13: warning: private val y in class X is never used t10790.scala:10: warning: private class C in class X is never used private class C // warn ^ -t10790.scala:8: warning: parameter value x in method control is never used +t10790.scala:8: warning: parameter x in method control is never used def control(x: Int) = answer // warn to verify control ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/t12720.check b/test/files/neg/t12720.check new file mode 100644 index 000000000000..603ba26094e4 --- /dev/null +++ b/test/files/neg/t12720.check @@ -0,0 +1,40 @@ +t12720.scala:15: error: missing argument list for method f*** in class D*** +Unapplied methods are only converted to functions when a function type is expected. +You can make this conversion explicit by writing `f _` or `f(_)` instead of `f`. + def f = d.f + ^ +t12720.scala:10: warning: private constructor in class Test*** is never used + private def this(stuff: Int) = this() + ^ +t12720.scala:20: warning: local method m*** in method forgone*** is never used + def m = 17 + ^ +t12720.scala:21: warning: local object j*** in method forgone*** is never used + object j + ^ +t12720.scala:22: warning: local var totally*** in method forgone*** is never used + var totally = 27 + ^ +t12720.scala:24: warning: local val z*** in method forgone*** is never used + val z = unset + ^ +t12720.scala:28: warning: private var misbegotten*** in class Test*** is never used + private var misbegotten: Int = 42 + ^ +t12720.scala:30: warning: private var forgotten (forgotten_=***) in class Test*** is never updated: consider using immutable val + private var forgotten: Int = 42 + ^ +t12720.scala:12: warning: private class Subtle*** in class Test*** is never used + private class Subtle + ^ +t12720.scala:23: warning: local var unset*** in method forgone*** is never updated: consider using immutable val + var unset: Int = 0 + ^ +t12720.scala:16: warning: parameter x*** in method g*** is never used + def g(x: Int) = println("error") + ^ +t12720.scala:25: warning: parameter y*** in anonymous function is never used + for (x <- Option(42); y <- Option(27) if x < i; res = x - y) yield res + ^ +11 warnings +1 error diff --git a/test/files/neg/t12720.scala b/test/files/neg/t12720.scala new file mode 100644 index 000000000000..48c659065c4f --- /dev/null +++ b/test/files/neg/t12720.scala @@ -0,0 +1,32 @@ +// scalac: -uniqid -Werror -Wunused +// hide: #\d+ +class C { + def f(x: Int) = "" +} +class D extends C { + def f(x: String) = "" +} +final class Test { + private def this(stuff: Int) = this() + + private class Subtle + + val d = new D + def f = d.f + def g(x: Int) = println("error") + + // -Vprint shows two symbols `y` + def forgone(i: Int) = { + def m = 17 + object j + var totally = 27 + var unset: Int = 0 + val z = unset + for (x <- Option(42); y <- Option(27) if x < i; res = x - y) yield res + } + + private var misbegotten: Int = 42 + def touch() = misbegotten = 17 + private var forgotten: Int = 42 + def fetch = forgotten +} diff --git a/test/files/neg/warn-unused-explicits.check b/test/files/neg/warn-unused-explicits.check index 58c6912d34c7..9238b072a5ed 100644 --- a/test/files/neg/warn-unused-explicits.check +++ b/test/files/neg/warn-unused-explicits.check @@ -1,4 +1,4 @@ -warn-unused-explicits.scala:9: warning: parameter value x in method warn is never used +warn-unused-explicits.scala:9: warning: parameter x in method warn is never used def warn(x: Int) = answer ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/warn-unused-implicits.check b/test/files/neg/warn-unused-implicits.check index ee8fdfd924c6..b5c08f8d311e 100644 --- a/test/files/neg/warn-unused-implicits.check +++ b/test/files/neg/warn-unused-implicits.check @@ -1,7 +1,7 @@ -warn-unused-implicits.scala:13: warning: parameter value s in method f is never used +warn-unused-implicits.scala:13: warning: parameter s in method f is never used )(implicit s: String): Int = { // warn ^ -warn-unused-implicits.scala:33: warning: parameter value s in method i is never used +warn-unused-implicits.scala:33: warning: parameter s in method i is never used def i(implicit s: String, t: Int) = t // yes, warn ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/warn-unused-params.check b/test/files/neg/warn-unused-params.check index de20119fff4f..f18c7815af1b 100644 --- a/test/files/neg/warn-unused-params.check +++ b/test/files/neg/warn-unused-params.check @@ -1,34 +1,34 @@ -warn-unused-params.scala:13: warning: parameter value b in method f is never used +warn-unused-params.scala:13: warning: parameter b in method f is never used b: String, // warn ^ -warn-unused-params.scala:36: warning: parameter value s in method i is never used +warn-unused-params.scala:36: warning: parameter s in method i is never used def i(implicit s: String) = answer // yes, warn ^ -warn-unused-params.scala:53: warning: parameter value u in class Unusing is never used +warn-unused-params.scala:53: warning: parameter u in class Unusing is never used class Unusing(u: Int) { // warn ^ -warn-unused-params.scala:63: warning: parameter value s in class CaseyAtTheBat is never used +warn-unused-params.scala:63: warning: parameter s in class CaseyAtTheBat is never used case class CaseyAtTheBat(k: Int)(s: String) // warn ^ -warn-unused-params.scala:66: warning: parameter value readResolve in method f is never used +warn-unused-params.scala:66: warning: parameter readResolve in method f is never used def f(readResolve: Int) = answer // warn ^ -warn-unused-params.scala:81: warning: parameter value dummy in method g is never used +warn-unused-params.scala:81: warning: parameter dummy in method g is never used def g(dummy: DummyImplicit) = answer ^ -warn-unused-params.scala:86: warning: parameter value ev in method f2 is never used +warn-unused-params.scala:86: warning: parameter ev in method f2 is never used def f2[A, B](ev: A =:= B) = answer ^ -warn-unused-params.scala:87: warning: parameter value ev in method g2 is never used +warn-unused-params.scala:87: warning: parameter ev in method g2 is never used def g2[A, B](ev: A <:< B) = answer ^ -warn-unused-params.scala:91: warning: parameter value i in anonymous function is never used +warn-unused-params.scala:91: warning: parameter i in anonymous function is never used def f = (i: Int) => answer // warn ^ -warn-unused-params.scala:97: warning: parameter value i in anonymous function is never used +warn-unused-params.scala:97: warning: parameter i in anonymous function is never used def g = for (i <- List(1)) yield answer // warn map.(i => 42) ^ -warn-unused-params.scala:101: warning: parameter value ctx in method f is never used +warn-unused-params.scala:101: warning: parameter ctx in method f is never used def f[A](implicit ctx: Context[A]) = answer ^ warn-unused-params.scala:102: warning: evidence parameter evidence$1 of type Context[A] in method g is never used From c3bcb1f67b36d08a82584174e3a2ba3af96b94cc Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 30 Jan 2023 21:16:07 +0100 Subject: [PATCH 141/261] test tasty reader against Scala 3.3.0-RC2 --- project/DottySupport.scala | 2 +- src/compiler/scala/tools/tasty/TastyFormat.scala | 2 +- src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala | 7 ++++++- .../src-3-app/TestExperimentalDefsPre.check | 6 +----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index 5113d6d950fa..e7f0c9ce0149 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.2.2" // TASTy version 28.2-0 + val supportedTASTyRelease = "3.3.0-RC2" // TASTy version 28.3-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease diff --git a/src/compiler/scala/tools/tasty/TastyFormat.scala b/src/compiler/scala/tools/tasty/TastyFormat.scala index a967b916ee51..0c06b899d389 100644 --- a/src/compiler/scala/tools/tasty/TastyFormat.scala +++ b/src/compiler/scala/tools/tasty/TastyFormat.scala @@ -35,7 +35,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 2 + final val MinorVersion: Int = 3 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing diff --git a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala index 546cdc15e23c..da7325eba4d9 100644 --- a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala +++ b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala @@ -53,7 +53,12 @@ class TastyHeaderUnpickler(reader: TastyReader) { compilerExperimental = ExperimentalVersion ) - check(validVersion, { + // TODO: remove this exception once we have a 3.3.0 + val dottyRCexception = validVersion || + (fileMajor == 28 && fileMinor == 3 && fileExperimental == 1 + && MajorVersion == 28 && MinorVersion == 3 && ExperimentalVersion == 0) + + check(dottyRCexception, { val signature = signatureString(fileMajor, fileMinor, fileExperimental) val toolingVersion = new String(bytes, toolingStart.index, toolingLength) val producedByAddendum = s"\nThe TASTy file was produced by $toolingVersion.$toolingAddendum" diff --git a/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check b/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check index 346149d4cade..bdd98c5ac849 100644 --- a/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check +++ b/test/tasty/neg-full-circle/src-3-app/TestExperimentalDefsPre.check @@ -14,8 +14,4 @@ 6 | class SubSubExperimental extends SubExperimentalNotExperimental | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |object ExperimentalDefsPre is marked @experimental and therefore may only be used in an experimental scope. --- Error: TestExperimentalDefsPre_fail.scala:6:8 -6 | class SubSubExperimental extends SubExperimentalNotExperimental - | ^ - |extension of experimental class SubExperimentalNotExperimental must have @experimental annotation -5 errors found +4 errors found From 0b582d585ed6f01075ae318a711d816b6bb69084 Mon Sep 17 00:00:00 2001 From: Rituraj Date: Wed, 25 Jan 2023 21:32:30 +0530 Subject: [PATCH 142/261] Small code improvements in build definition --- project/DottySupport.scala | 4 ++-- project/GenerateFunctionConverters.scala | 2 +- project/JitWatch.scala | 2 +- project/Osgi.scala | 2 +- project/ParserUtil.scala | 4 ++-- project/PartestTestListener.scala | 5 ++--- project/PartestUtil.scala | 2 +- project/SavedLogs.scala | 4 ++-- project/VersionUtil.scala | 4 ++-- project/genprod.scala | 4 ++-- 10 files changed, 16 insertions(+), 17 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index e7f0c9ce0149..84217ec18d85 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -5,7 +5,7 @@ import sbt.Keys._ import java.io.File import sbt.librarymanagement.{ - ivy, DependencyResolution, ScalaModuleInfo, UpdateConfiguration, UnresolvedWarningConfiguration + DependencyResolution, ScalaModuleInfo, UpdateConfiguration, UnresolvedWarningConfiguration } /** @@ -28,7 +28,7 @@ object TastySupport { object DottySupport { val dottyVersion = "3.2.0" val compileWithDotty: Boolean = - Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false) + Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( Compile / scalacOptions ++= Seq( "-language:implicitConversions" // Avoid a million warnings diff --git a/project/GenerateFunctionConverters.scala b/project/GenerateFunctionConverters.scala index d1fbd334420e..52e02adfbb41 100644 --- a/project/GenerateFunctionConverters.scala +++ b/project/GenerateFunctionConverters.scala @@ -341,7 +341,7 @@ object GenerateFunctionConverters { def sourceFile(subPack: String, body: String): String = s"""$copyright | - |${packaging}${subPack} + |$packaging$subPack | |$body |""".stripMargin diff --git a/project/JitWatch.scala b/project/JitWatch.scala index d14c43765102..08b2c03eba0d 100644 --- a/project/JitWatch.scala +++ b/project/JitWatch.scala @@ -58,7 +58,7 @@ object JitWatchFilePlugin extends AutoPlugin { // Download and add transitive sources from the classpath val classiferArtifacts: Seq[(ModuleID, Artifact, File)] = updateClassifiers.value.configurations.flatMap(_.details.flatMap(_.modules.flatMap(report => report.artifacts.map(x => (report.module, x._1, x._2))))) - val sourceClassiferArtifacts = classiferArtifacts.filter(tuple => tuple._2.classifier == Some("sources") && dependencyModuleIds.contains(tuple._1)) + val sourceClassiferArtifacts = classiferArtifacts.filter(tuple => tuple._2.classifier.contains("sources") && dependencyModuleIds.contains(tuple._1)) val externalSources = sourceClassiferArtifacts.map(_._3) val internalAndExternalSources = sourceDirectories.value ++ (javaHomeSrc +: (transitiveSourceDirectories ++ transitiveSourceDirectories2).distinct) ++ externalSources diff --git a/project/Osgi.scala b/project/Osgi.scala index 57296107fa1a..e745872e76de 100644 --- a/project/Osgi.scala +++ b/project/Osgi.scala @@ -85,7 +85,7 @@ object Osgi { def resourceDirectoryRef(f: File) = (if (f.getName endsWith ".jar") "@" else "") + f.getAbsolutePath val includeRes = resourceDirectories.filter(_.exists).map(resourceDirectoryRef).mkString(",") - if (!includeRes.isEmpty) builder.setProperty(INCLUDERESOURCE, includeRes) + if (includeRes.nonEmpty) builder.setProperty(INCLUDERESOURCE, includeRes) builder.getProperties.asScala.foreach { case (k, v) => log.debug(s"bnd: $k: $v") } // builder.build is not thread-safe because it uses a static SimpleDateFormat. This ensures // that all calls to builder.build are serialized. diff --git a/project/ParserUtil.scala b/project/ParserUtil.scala index 311544d108c7..ea921031c89c 100644 --- a/project/ParserUtil.scala +++ b/project/ParserUtil.scala @@ -23,8 +23,8 @@ object ParserUtil { val preFile = if (prefixIsAbsolute) prefixFile else new File(base, prefix) val basePrefix = if (prefixIsAbsolute) "" else ensureSuffix(base.getPath, "/") def relativize(p: String) = p.stripPrefix(basePrefix) - def pathOf(f: File) = if (f.isDirectory() && !fileFilter.accept(f)) ensureSuffix(f.getPath, "/") else f.getPath - val finder = if (preFile.isDirectory()) { + def pathOf(f: File) = if (f.isDirectory && !fileFilter.accept(f)) ensureSuffix(f.getPath, "/") else f.getPath + val finder = if (preFile.isDirectory) { preFile.glob(childFilter) } else if (preFile.exists()) { PathFinder(preFile).filter(fileFilter.accept) diff --git a/project/PartestTestListener.scala b/project/PartestTestListener.scala index 83d1e82aefd8..f7df4ab3f148 100644 --- a/project/PartestTestListener.scala +++ b/project/PartestTestListener.scala @@ -1,7 +1,6 @@ package scala.build import java.io.{File, PrintWriter, StringWriter} -import java.util.concurrent.TimeUnit import sbt.testing.{SuiteSelector, TestSelector} import sbt.{JUnitXmlTestsListener, TestEvent, TestResult, TestsListener, _} @@ -47,7 +46,7 @@ class PartestTestListener(target: File) extends TestsListener { e.fullyQualifiedName() } - for ((group, events) <- event.detail.groupBy(groupOf(_))) { + for ((group, events) <- event.detail.groupBy(groupOf)) { val statii = events.map(_.status()) val errorCount = statii.count(errorStatus.contains) val failCount = statii.count(failStatus.contains) @@ -95,7 +94,7 @@ class PartestTestListener(target: File) extends TestsListener { }} val partestTestReports = target / "test-reports" / "partest" - val xmlFile = (partestTestReports / (group + ".xml")) + val xmlFile = partestTestReports / (group + ".xml") xmlFile.getParentFile.mkdirs() scala.xml.XML.save(xmlFile.getAbsolutePath, xml, "UTF-8", true, null) } diff --git a/project/PartestUtil.scala b/project/PartestUtil.scala index 79dceaf4700b..672b70a60fa7 100644 --- a/project/PartestUtil.scala +++ b/project/PartestUtil.scala @@ -10,7 +10,7 @@ object PartestUtil { val srcDir = testBase / srcPath // mirror of partest.nest.PathSettings#srcDir private val testCaseFile = GlobFilter("*.scala") | GlobFilter("*.java") | GlobFilter("*.res") - private val testCaseDir = new SimpleFileFilter(f => f.isDirectory() && f.listFiles().nonEmpty && !(f.getParentFile / (f.getName + ".res")).exists()) + private val testCaseDir = new SimpleFileFilter(f => f.isDirectory && f.listFiles().nonEmpty && !(f.getParentFile / (f.getName + ".res")).exists()) private val testCaseFilter = testCaseFile || testCaseDir private val testCaseFinder = srcDir * AllPassFilter * testCaseFilter diff --git a/project/SavedLogs.scala b/project/SavedLogs.scala index 27e1277da670..4ec335f4b2bf 100644 --- a/project/SavedLogs.scala +++ b/project/SavedLogs.scala @@ -1,10 +1,10 @@ package scala.build -import java.io.{ByteArrayOutputStream, PrintStream, StringWriter} +import java.io.{ByteArrayOutputStream, PrintStream} import sbt._ import Keys._ -import sbt.internal.util.{ConsoleAppender, StringEvent } +import sbt.internal.util.ConsoleAppender import scala.collection.mutable /** Save MiMa logs so they don't get lost in lots of debug output */ diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index 78b05f0c4685..f177278ee5db 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -78,7 +78,7 @@ object VersionUtil { // Workaround lack of git worktree support in JGit https://bugs.eclipse.org/bugs/show_bug.cgi?id=477475 val sha = List("git", "rev-parse", "HEAD").!!.trim val commitDateIso = List("git", "log", "-1", "--format=%cI", "HEAD").!!.trim - val date = Date.from(ISO_DATE_TIME.parse(commitDateIso, Instant.from(_))) + val date = Date.from(ISO_DATE_TIME.parse(commitDateIso, Instant.from _)) (date, sha.substring(0, 7)) } catch { case ex: Exception => @@ -127,7 +127,7 @@ object VersionUtil { val (base, suffix) = { val (b, s) = (baseVersion.value, baseVersionSuffix.value) if(s == "SPLIT") { - val split = """([\w+\.]+)(-[\w+\.-]+)??""".r + val split = """([\w+.]+)(-[\w+.-]+)??""".r val split(b2, sOrNull) = b (b2, Option(sOrNull).map(_.drop(1)).getOrElse("")) } else (b, s) diff --git a/project/genprod.scala b/project/genprod.scala index 1d37ee74cddf..565281847084 100644 --- a/project/genprod.scala +++ b/project/genprod.scala @@ -217,7 +217,7 @@ class Function(val i: Int) extends Group("Function") with Arity { * object Main extends App {%s} * }}}""" - def toStr() = "\"" + ("" format i) + "\"" + def toStr = "\"" + ("" format i) + "\"" def apply() = { {header} {companionObject} @@ -392,7 +392,7 @@ class Product(val i: Int) extends Group("Product") with Arity { * * @param n number of the projection to be returned * @return same as `._(n+1)`, for example `productElement(0)` is the same as `._1`. - * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= ${i}). + * @throws IndexOutOfBoundsException if the `n` is out of range(n < 0 || n >= $i). */ """ From d1affdcc82272bd6c015ec2fcac4f1fbc3e0bc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E5=A2=83=E8=BF=B7=E7=A6=BB?= Date: Tue, 27 Sep 2022 15:17:48 +0800 Subject: [PATCH 143/261] make `ValueOf` support specialization The constraint type of the type parameter supports specialization, and there are `specialized` annotation. It should support specialization. fixes scala/bug#11489 --- .../scala/tools/nsc/transform/SpecializeTypes.scala | 9 ++++++++- test/files/specialized/t11489.scala | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/files/specialized/t11489.scala diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 8d4d9f8417c4..705f1cdcc14d 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -506,8 +506,15 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { else if (!args.isEmpty) enteringTyper { foreach2(sym.typeParams, args) { (tp, arg) => - if (tp.isSpecialized) + if (tp.isSpecialized) { specializedTypeVarsBuffer(arg, result) + } else if (sym == ValueOfClass) { // scala/bug#11489, we only update it for ValueOf + arg.typeSymbol.annotations.foreach { + case lzai: LazyAnnotationInfo if lzai.symbol == SpecializedClass => + specializedTypeVarsBuffer(arg, result) + case _ => + } + } } } case PolyType(tparams, resTpe) => specializedTypeVarsBuffer(resTpe, result); tparams.foreach(sym => specializedTypeVarsBuffer(sym.info, result)) diff --git a/test/files/specialized/t11489.scala b/test/files/specialized/t11489.scala new file mode 100644 index 000000000000..5ccd04ddb2a4 --- /dev/null +++ b/test/files/specialized/t11489.scala @@ -0,0 +1,8 @@ +class Parent { + def twice[@scala.specialized(Int) I <: Int : ValueOf]: Int = valueOf[I] * 2 +} + +object Test extends App { + val actualMethods = (new Parent).getClass.getDeclaredMethods + assert(actualMethods.count(_.getName == "twice$mIc$sp") == 1) +} \ No newline at end of file From f443aebdda0cda2d821888bd2f8034ce1edf9be9 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 31 Jan 2023 12:24:44 +0100 Subject: [PATCH 144/261] [backport] -md md5 when decrypting credentials Backport of https://github.com/scala/scala/pull/9384. --- admin/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/init.sh b/admin/init.sh index a07934cec719..d75db254fc3e 100755 --- a/admin/init.sh +++ b/admin/init.sh @@ -5,7 +5,7 @@ sensitive() { perl -p -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < files/credentials-sonatype > ~/.credentials-sonatype perl -p -e 's/\$\{([^}]+)\}/defined $ENV{$1} ? $ENV{$1} : $&/eg' < files/sonatype-curl > ~/.sonatype-curl - openssl aes-256-cbc -d -pass "pass:$GPG_SUBKEY_SECRET" -in files/gpg_subkey.enc | gpg --import + openssl aes-256-cbc -md md5 -d -pass "pass:$GPG_SUBKEY_SECRET" -in files/gpg_subkey.enc | gpg --import } # don't let anything escape from the sensitive part (e.g. leak environment var by echoing to log on failure) From 64549ea198494606c6513892ee76062a18ad5225 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 30 Jan 2023 10:06:33 -0800 Subject: [PATCH 145/261] toolArgs caches file headers --- .../scala/tools/partest/nest/Runner.scala | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 17e672203190..3b7dd3b1718a 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -16,26 +16,26 @@ package nest import java.io.{Console => _, _} import java.lang.reflect.InvocationTargetException import java.nio.charset.Charset -import java.nio.file.{Files, StandardOpenOption} +import java.nio.file.{Files, Path, StandardOpenOption} import scala.annotation.nowarn -import scala.collection.mutable.ListBuffer +import scala.collection.mutable, mutable.ListBuffer import scala.concurrent.duration.Duration import scala.reflect.internal.FatalError -import scala.reflect.internal.util.ScalaClassLoader +import scala.reflect.internal.util.ScalaClassLoader, ScalaClassLoader.URLClassLoader import scala.sys.process.{Process, ProcessLogger} import scala.tools.nsc.Properties.{isWin, propOrEmpty} import scala.tools.nsc.{CompilerCommand, Global, Settings} import scala.tools.nsc.reporters.ConsoleReporter import scala.tools.nsc.util.stackTraceString -import ClassPath.join -import TestState.{Crash, Fail, Pass, Skip, Updated} -import FileManager.{compareContents, joinPaths, withTempFile} -import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader import scala.util.{Failure, Success, Try, Using} import scala.util.Properties.isJavaAtLeast import scala.util.chaining._ import scala.util.control.ControlThrowable +import scala.util.matching.Regex +import ClassPath.join +import FileManager.{compareContents, joinPaths, withTempFile} +import TestState.{Crash, Fail, Pass, Skip, Updated} /** pos/t1234.scala or pos/t1234 if dir */ case class TestInfo(testFile: File) { @@ -352,37 +352,33 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { * any Windows backslashes with the one true file separator char. */ def normalizeLog(): Unit = { - import scala.util.matching.Regex - // Apply judiciously; there are line comments in the "stub implementations" error output. - val slashes = """[/\\]+""".r + val slashes = """[/\\]+""".r def squashSlashes(s: String) = slashes.replaceAllIn(s, "/") // this string identifies a path and is also snipped from log output. - val elided = parentFile.getAbsolutePath + val elided = parentFile.getAbsolutePath // something to mark the elision in the log file (disabled) - val ellipsis = "" //".../" // using * looks like a comment + val ellipsis = "" //".../" // using * looks like a comment // no spaces in test file paths below root, because otherwise how to detect end of path string? val pathFinder = raw"""(?i)\Q${elided}${File.separator}\E([\${File.separator}\S]*)""".r def canonicalize(s: String): String = pathFinder.replaceAllIn(s, m => Regex.quoteReplacement(ellipsis + squashSlashes(m group 1))) - def masters = { + def masters = { val files = List(new File(parentFile, "filters"), new File(suiteRunner.pathSettings.srcDir.path, "filters")) - files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filter(s => !(s startsWith "#")) + files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filterNot(_.startsWith("#")) } - val filters = toolArgs("filter", split = false) ++ masters - val elisions = ListBuffer[String]() - //def lineFilter(s: String): Boolean = !(filters exists (s contains _)) - def lineFilter(s: String): Boolean = ( - filters map (_.r) forall { r => - val res = (r findFirstIn s).isEmpty - if (!res) elisions += s - res + val filters = toolArgs("filter", split = false) ++ masters + lazy val elisions = ListBuffer[String]() + def lineFilter(s: String): Boolean = + filters.map(_.r).forall { r => + val unfiltered = r.findFirstIn(s).isEmpty + if (!unfiltered && suiteRunner.verbose) elisions += s + unfiltered } - ) logFile.mapInPlace(canonicalize)(lineFilter) if (suiteRunner.verbose && elisions.nonEmpty) { @@ -462,24 +458,26 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { def toolArgs(tool: String, split: Boolean = true): List[String] = toolArgsFor(sources(testFile))(tool, split) + // cache the headers we read for toolArgs + private val fileHeaders = new mutable.HashMap[Path, List[String]] + // inspect given files for tool args of the form `tool: args` // if args string ends in close comment, drop the `*` `/` // if split, parse the args string as command line. + // Currently, we look for scalac, javac, java, javaVersion, filter, hide. // def toolArgsFor(files: List[File])(tool: String, split: Boolean = true): List[String] = { - def argsFor(f: File): List[String] = { - import scala.jdk.OptionConverters._ - import scala.sys.process.{Parser => CommandLineParser}, CommandLineParser.tokenize - val max = 10 + def argsFor(f: File): List[String] = fromHeader(fileHeaders.getOrElseUpdate(f.toPath, readHeaderFrom(f))) + def fromHeader(header: List[String]) = { + import scala.sys.process.Parser.tokenize val tag = s"$tool:" val endc = "*" + "/" // be forgiving of /* scalac: ... */ def stripped(s: String) = s.substring(s.indexOf(tag) + tag.length).stripSuffix(endc) def argsplitter(s: String) = if (split) tokenize(s) else List(s.trim()) - val args = Using.resource(Files.lines(f.toPath, codec.charSet))( - _.limit(max).filter(_.contains(tag)).map(stripped).findAny.toScala - ) - args.map(argsplitter).getOrElse(Nil) + header.filter(_.contains(tag)).map(stripped).flatMap(argsplitter) } + def readHeaderFrom(f: File): List[String] = + Using.resource(Files.lines(f.toPath, codec.charSet))(stream => stream.limit(10).toArray()).toList.map(_.toString) files.flatMap(argsFor) } From a6ccbe8e1df2d5499640c7ca70dec429ec9e98b7 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 31 Jan 2023 09:47:58 -0800 Subject: [PATCH 146/261] Use tool name --- .../scala/tools/partest/nest/Runner.scala | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 3b7dd3b1718a..681616cd325b 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -113,7 +113,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { joinPaths(outDir :: testClassPath), "-J-Duser.language=en", "-J-Duser.country=US" - ) ++ (toolArgsFor(files)("javac") + ) ++ (toolArgsFor(files)(ToolName.javac) ) ++ (files.map(_.getAbsolutePath) ) @@ -371,7 +371,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { val files = List(new File(parentFile, "filters"), new File(suiteRunner.pathSettings.srcDir.path, "filters")) files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filterNot(_.startsWith("#")) } - val filters = toolArgs("filter", split = false) ++ masters + val filters = toolArgs(ToolName.filter, split = false) ++ masters lazy val elisions = ListBuffer[String]() def lineFilter(s: String): Boolean = filters.map(_.r).forall { r => @@ -445,18 +445,17 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // under --realeasy, if a javaVersion isn't specified, require the minimum viable using -release 8 // to avoid accidentally committing a test that requires a later JVM. def flagsForCompilation(sources: List[File]): List[String] = { - var perFile = toolArgsFor(sources)("scalac") + var perFile = toolArgsFor(sources)(ToolName.scalac) if (parentFile.getParentFile.getName == "macro-annot") perFile ::= "-Ymacro-annotations" - if (realeasy && isJavaAtLeast(9) && !perFile.exists(releaseFlag.matches) && toolArgsFor(sources)("javaVersion", split = false).isEmpty) + if (realeasy && isJavaAtLeast(9) && !perFile.exists(releaseFlag.matches) && toolArgsFor(sources)(ToolName.javaVersion, split = false).isEmpty) perFile ::= "-release:8" perFile } private val releaseFlag = raw"--?release(?::\d+)?".r // inspect sources for tool args - def toolArgs(tool: String, split: Boolean = true): List[String] = - toolArgsFor(sources(testFile))(tool, split) + def toolArgs(tool: ToolName, split: Boolean = true): List[String] = toolArgsFor(sources(testFile))(tool, split) // cache the headers we read for toolArgs private val fileHeaders = new mutable.HashMap[Path, List[String]] @@ -466,7 +465,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // if split, parse the args string as command line. // Currently, we look for scalac, javac, java, javaVersion, filter, hide. // - def toolArgsFor(files: List[File])(tool: String, split: Boolean = true): List[String] = { + def toolArgsFor(files: List[File])(tool: ToolName, split: Boolean = true): List[String] = { def argsFor(f: File): List[String] = fromHeader(fileHeaders.getOrElseUpdate(f.toPath, readHeaderFrom(f))) def fromHeader(header: List[String]) = { import scala.sys.process.Parser.tokenize @@ -513,7 +512,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { val Range = """(\d+)(?:(\+)|(?: *\- *(\d+)))?""".r lazy val currentJavaVersion = javaSpecVersion.stripPrefix("1.").toInt val allFiles = sources(file) - val skipStates = toolArgsFor(allFiles)("javaVersion", split = false).flatMap({ + val skipStates = toolArgsFor(allFiles)(ToolName.javaVersion, split = false).flatMap({ case v @ Range(from, plus, to) => val ok = if (plus == null) @@ -655,7 +654,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } private def runRunTest(): TestState = { - val javaopts = toolArgs("java") + val javaopts = toolArgs(ToolName.java) val execInProcess = PartestDefaults.execInProcess && javaopts.isEmpty && !Set("specialized", "instrumented").contains(testFile.getParentFile.getName) def exec() = if (execInProcess) execTestInProcess(outDir, logFile) else execTest(outDir, logFile, javaopts) def noexec() = genSkip("no-exec: tests compiled but not run") @@ -766,3 +765,16 @@ final class TestTranscript { def append(text: String): Unit = { val s = buf.last ; buf.dropRightInPlace(1) ; buf += (s + text) } def toList = buf.toList } + +// Tool names in test file header: scalac, javac, java, javaVersion, filter, hide. +sealed trait ToolName +object ToolName { + case object scalac extends ToolName + case object javac extends ToolName + case object java extends ToolName + case object javaVersion extends ToolName + case object filter extends ToolName + case object hide extends ToolName + private val values = Array(scalac, javac, java, javaVersion, filter, hide) + def named(s: String): ToolName = values.find(_.toString.equalsIgnoreCase(s)).getOrElse(throw new IllegalArgumentException(s)) +} From 60394a90795cbcd585cf4c6ad6a49ddfec21a97d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 31 Jan 2023 11:51:07 -0800 Subject: [PATCH 147/261] Cache tool args directly (unparsed and parsed) --- .../scala/tools/partest/nest/Runner.scala | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 681616cd325b..b2f7923b3f2a 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -457,8 +457,8 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // inspect sources for tool args def toolArgs(tool: ToolName, split: Boolean = true): List[String] = toolArgsFor(sources(testFile))(tool, split) - // cache the headers we read for toolArgs - private val fileHeaders = new mutable.HashMap[Path, List[String]] + // cache the arg lines and split args per tool for each file + private val fileToolArgs = new mutable.HashMap[Path, Map[ToolName, (List[String], List[String])]] // inspect given files for tool args of the form `tool: args` // if args string ends in close comment, drop the `*` `/` @@ -466,14 +466,21 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // Currently, we look for scalac, javac, java, javaVersion, filter, hide. // def toolArgsFor(files: List[File])(tool: ToolName, split: Boolean = true): List[String] = { - def argsFor(f: File): List[String] = fromHeader(fileHeaders.getOrElseUpdate(f.toPath, readHeaderFrom(f))) - def fromHeader(header: List[String]) = { + def argsFor(f: File): List[String] = fileToolArgs.getOrElseUpdate(f.toPath, readToolArgs(f)).get(tool) match { + case Some((lines, args)) => if (split) args else lines + case _ => Nil + } + def readToolArgs(f: File): Map[ToolName, (List[String], List[String])] = { + val header = readHeaderFrom(f) + ToolName.values.toList.map(name => (name, fromHeader(name, header))).filterNot(_._2._1.isEmpty).toMap + } + def fromHeader(name: ToolName, header: List[String]) = { import scala.sys.process.Parser.tokenize - val tag = s"$tool:" + val tag = s"$name:" val endc = "*" + "/" // be forgiving of /* scalac: ... */ def stripped(s: String) = s.substring(s.indexOf(tag) + tag.length).stripSuffix(endc) - def argsplitter(s: String) = if (split) tokenize(s) else List(s.trim()) - header.filter(_.contains(tag)).map(stripped).flatMap(argsplitter) + val lines = header.filter(_.contains(tag)).map(line => stripped(line).trim()) + (lines, lines.flatMap(tokenize)) } def readHeaderFrom(f: File): List[String] = Using.resource(Files.lines(f.toPath, codec.charSet))(stream => stream.limit(10).toArray()).toList.map(_.toString) @@ -509,10 +516,10 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { def compilationRounds(file: File): List[CompileRound] = { import scala.util.Properties.javaSpecVersion - val Range = """(\d+)(?:(\+)|(?: *\- *(\d+)))?""".r + val Range = """(\d+)(?:(\+)|(?:-(\d+)))?""".r lazy val currentJavaVersion = javaSpecVersion.stripPrefix("1.").toInt val allFiles = sources(file) - val skipStates = toolArgsFor(allFiles)(ToolName.javaVersion, split = false).flatMap({ + val skipStates = toolArgsFor(allFiles)(ToolName.javaVersion).flatMap { case v @ Range(from, plus, to) => val ok = if (plus == null) @@ -524,7 +531,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { else Some(genSkip(s"skipped on Java $javaSpecVersion, only running on $v")) case v => Some(genFail(s"invalid javaVersion range in test comment: $v")) - }) + } skipStates.headOption match { case Some(state) => List(SkipRound(List(file), state)) case _ => groupedFiles(allFiles).flatMap(mixedCompileGroup) @@ -775,6 +782,6 @@ object ToolName { case object javaVersion extends ToolName case object filter extends ToolName case object hide extends ToolName - private val values = Array(scalac, javac, java, javaVersion, filter, hide) + val values = Array(scalac, javac, java, javaVersion, filter, hide) def named(s: String): ToolName = values.find(_.toString.equalsIgnoreCase(s)).getOrElse(throw new IllegalArgumentException(s)) } From b333865f4c9431159a46f836bc84515a34534c23 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 31 Jan 2023 13:26:35 -0800 Subject: [PATCH 148/261] Only filter tool arg is unparsed --- .../scala/tools/partest/nest/Runner.scala | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index b2f7923b3f2a..8cb9ff65ea30 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -371,7 +371,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { val files = List(new File(parentFile, "filters"), new File(suiteRunner.pathSettings.srcDir.path, "filters")) files.filter(_.exists).flatMap(_.fileLines).map(_.trim).filterNot(_.startsWith("#")) } - val filters = toolArgs(ToolName.filter, split = false) ++ masters + val filters = toolArgs(ToolName.filter) ++ masters lazy val elisions = ListBuffer[String]() def lineFilter(s: String): Boolean = filters.map(_.r).forall { r => @@ -448,31 +448,32 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { var perFile = toolArgsFor(sources)(ToolName.scalac) if (parentFile.getParentFile.getName == "macro-annot") perFile ::= "-Ymacro-annotations" - if (realeasy && isJavaAtLeast(9) && !perFile.exists(releaseFlag.matches) && toolArgsFor(sources)(ToolName.javaVersion, split = false).isEmpty) + if (realeasy && isJavaAtLeast(9) && !perFile.exists(releaseFlag.matches) && toolArgsFor(sources)(ToolName.javaVersion).isEmpty) perFile ::= "-release:8" perFile } private val releaseFlag = raw"--?release(?::\d+)?".r // inspect sources for tool args - def toolArgs(tool: ToolName, split: Boolean = true): List[String] = toolArgsFor(sources(testFile))(tool, split) + def toolArgs(tool: ToolName): List[String] = toolArgsFor(sources(testFile))(tool) - // cache the arg lines and split args per tool for each file - private val fileToolArgs = new mutable.HashMap[Path, Map[ToolName, (List[String], List[String])]] + // for each file, cache the args for each tool + private val fileToolArgs = new mutable.HashMap[Path, Map[ToolName, List[String]]] // inspect given files for tool args of the form `tool: args` // if args string ends in close comment, drop the `*` `/` - // if split, parse the args string as command line. + // if filter, return entire line as if quoted, else parse the args string as command line. // Currently, we look for scalac, javac, java, javaVersion, filter, hide. // - def toolArgsFor(files: List[File])(tool: ToolName, split: Boolean = true): List[String] = { - def argsFor(f: File): List[String] = fileToolArgs.getOrElseUpdate(f.toPath, readToolArgs(f)).get(tool) match { - case Some((lines, args)) => if (split) args else lines - case _ => Nil - } - def readToolArgs(f: File): Map[ToolName, (List[String], List[String])] = { + def toolArgsFor(files: List[File])(tool: ToolName): List[String] = { + def argsFor(f: File): List[String] = fileToolArgs.getOrElseUpdate(f.toPath, readToolArgs(f)).apply(tool) + def readToolArgs(f: File): Map[ToolName, List[String]] = { val header = readHeaderFrom(f) - ToolName.values.toList.map(name => (name, fromHeader(name, header))).filterNot(_._2._1.isEmpty).toMap + ToolName.values.toList + .map(name => name -> fromHeader(name, header)) + .filterNot(_._2.isEmpty) + .toMap[ToolName, List[String]] + .withDefaultValue(List.empty[String]) } def fromHeader(name: ToolName, header: List[String]) = { import scala.sys.process.Parser.tokenize @@ -480,7 +481,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { val endc = "*" + "/" // be forgiving of /* scalac: ... */ def stripped(s: String) = s.substring(s.indexOf(tag) + tag.length).stripSuffix(endc) val lines = header.filter(_.contains(tag)).map(line => stripped(line).trim()) - (lines, lines.flatMap(tokenize)) + if (name == ToolName.filter) lines else lines.flatMap(tokenize) } def readHeaderFrom(f: File): List[String] = Using.resource(Files.lines(f.toPath, codec.charSet))(stream => stream.limit(10).toArray()).toList.map(_.toString) From f66194c19673db1b7adb55ec3e95a54e381220af Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 4 Feb 2023 19:47:30 -0800 Subject: [PATCH 149/261] Tweak class doc for StringBuilder --- .../collection/mutable/StringBuilder.scala | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/library/scala/collection/mutable/StringBuilder.scala b/src/library/scala/collection/mutable/StringBuilder.scala index e5b271061e6a..d8b5966158f2 100644 --- a/src/library/scala/collection/mutable/StringBuilder.scala +++ b/src/library/scala/collection/mutable/StringBuilder.scala @@ -24,17 +24,30 @@ import scala.Predef.{ // unimport char-related implicit conversions to avoid tri //_ } -/** A builder for mutable sequence of characters. This class provides an API - * mostly compatible with `java.lang.StringBuilder`, except where there are - * conflicts with the Scala collections API (such as the `reverse` method.) - * - * $multipleResults - * - * @define Coll `mutable.IndexedSeq` - * @define coll string builder - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] - * section on `StringBuilders` for more information. - */ +/** A builder of `String` which is also a mutable sequence of characters. + * + * This class provides an API mostly compatible with `java.lang.StringBuilder`, + * except where there are conflicts with the Scala collections API, such as the `reverse` method: + * [[reverse]] produces a new `StringBuilder`, and [[reverseInPlace]] mutates this builder. + * + * Mutating operations return either `this.type`, i.e., the current builder, or `Unit`. + * + * Other methods extract data or information from the builder without mutating it. + * + * The distinction is also reflected in naming conventions used by collections, + * such as `append`, which mutates, and `appended`, which does not, or `reverse`, + * which does not mutate, and `reverseInPlace`, which does. + * + * The `String` result may be obtained using either `result()` or `toString`. + * + * $multipleResults + * + * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] + * section on `StringBuilders` for more information. + * + * @define Coll `mutable.IndexedSeq` + * @define coll string builder + */ @SerialVersionUID(3L) final class StringBuilder(val underlying: java.lang.StringBuilder) extends AbstractSeq[Char] with ReusableBuilder[Char, String] From c481dae4420bffdd0902c6ffb3442b95f4012ac6 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 4 Feb 2023 20:04:16 -0800 Subject: [PATCH 150/261] Update overview/collections URL in doc --- src/library/scala/Array.scala | 2 +- src/library/scala/collection/concurrent/Map.scala | 2 +- src/library/scala/collection/immutable/BitSet.scala | 2 +- src/library/scala/collection/immutable/LazyList.scala | 2 +- src/library/scala/collection/immutable/List.scala | 2 +- src/library/scala/collection/immutable/ListMap.scala | 2 +- src/library/scala/collection/immutable/Queue.scala | 2 +- src/library/scala/collection/immutable/TreeMap.scala | 2 +- src/library/scala/collection/immutable/TreeSet.scala | 2 +- src/library/scala/collection/mutable/ArrayBuffer.scala | 2 +- src/library/scala/collection/mutable/BitSet.scala | 2 +- .../scala/collection/mutable/CollisionProofHashMap.scala | 2 +- src/library/scala/collection/mutable/HashMap.scala | 2 +- src/library/scala/collection/mutable/HashSet.scala | 2 +- src/library/scala/collection/mutable/ListBuffer.scala | 2 +- src/library/scala/collection/mutable/StringBuilder.scala | 2 +- src/library/scala/collection/mutable/WeakHashMap.scala | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala index c6266e6ef1c4..6544548efdec 100644 --- a/src/library/scala/Array.scala +++ b/src/library/scala/Array.scala @@ -624,7 +624,7 @@ object Array { * * @see [[https://www.scala-lang.org/files/archive/spec/2.13/ Scala Language Specification]], for in-depth information on the transformations the Scala compiler makes on Arrays (Sections 6.6 and 6.15 respectively.) * @see [[https://docs.scala-lang.org/sips/scala-2-8-arrays.html "Scala 2.8 Arrays"]] the Scala Improvement Document detailing arrays since Scala 2.8. - * @see [[https://docs.scala-lang.org/overviews/collections/arrays.html "The Scala 2.8 Collections' API"]] section on `Array` by Martin Odersky for more information. + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/arrays.html "The Scala 2.8 Collections' API"]] section on `Array` by Martin Odersky for more information. * @hideImplicitConversion scala.Predef.booleanArrayOps * @hideImplicitConversion scala.Predef.byteArrayOps * @hideImplicitConversion scala.Predef.charArrayOps diff --git a/src/library/scala/collection/concurrent/Map.scala b/src/library/scala/collection/concurrent/Map.scala index 2fd444035bf5..c2b996b93102 100644 --- a/src/library/scala/collection/concurrent/Map.scala +++ b/src/library/scala/collection/concurrent/Map.scala @@ -19,7 +19,7 @@ import scala.annotation.tailrec * * $concurrentmapinfo * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#concurrent_maps "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#concurrent_maps "Scala's Collection Library overview"]] * section on `Concurrent Maps` for more information. * * @tparam K the key type of the map diff --git a/src/library/scala/collection/immutable/BitSet.scala b/src/library/scala/collection/immutable/BitSet.scala index cc99f9907078..9461264850a9 100644 --- a/src/library/scala/collection/immutable/BitSet.scala +++ b/src/library/scala/collection/immutable/BitSet.scala @@ -20,7 +20,7 @@ import scala.annotation.{implicitNotFound, nowarn} /** A class for immutable bitsets. * $bitsetinfo - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#immutable-bitsets "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#immutable-bitsets "Scala's Collection Library overview"]] * section on `Immutable BitSets` for more information. * * @define Coll `immutable.BitSet` diff --git a/src/library/scala/collection/immutable/LazyList.scala b/src/library/scala/collection/immutable/LazyList.scala index d9d52e8da44b..8b7ad26dc5ae 100644 --- a/src/library/scala/collection/immutable/LazyList.scala +++ b/src/library/scala/collection/immutable/LazyList.scala @@ -220,7 +220,7 @@ import scala.runtime.Statics * * @tparam A the type of the elements contained in this lazy list. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] * section on `LazyLists` for more information. * @define Coll `LazyList` * @define coll lazy list diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index dc117a0bdb72..5358922752fb 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -65,7 +65,7 @@ import scala.runtime.Statics.releaseFence * objects that rely on structural sharing), will be serialized and deserialized with multiple lists, one for * each reference to it. I.e. structural sharing is lost after serialization/deserialization. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#lists "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lists "Scala's Collection Library overview"]] * section on `Lists` for more information. * * @define coll list diff --git a/src/library/scala/collection/immutable/ListMap.scala b/src/library/scala/collection/immutable/ListMap.scala index 68496f445bc3..4a2b8dbd807c 100644 --- a/src/library/scala/collection/immutable/ListMap.scala +++ b/src/library/scala/collection/immutable/ListMap.scala @@ -119,7 +119,7 @@ sealed class ListMap[K, +V] * n elements will take O(n^2^) time. This makes the builder suitable only for a small number of * elements. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#list-maps "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#list-maps "Scala's Collection Library overview"]] * section on `List Maps` for more information. * @define Coll ListMap * @define coll list map diff --git a/src/library/scala/collection/immutable/Queue.scala b/src/library/scala/collection/immutable/Queue.scala index eb12f6fd8b14..3d0f8206b6a9 100644 --- a/src/library/scala/collection/immutable/Queue.scala +++ b/src/library/scala/collection/immutable/Queue.scala @@ -27,7 +27,7 @@ import scala.collection.mutable.{Builder, ListBuffer} * where a pivot is required, in which case, a cost of `O(n)` is incurred, where `n` is the number of elements in the queue. When this happens, * `n` remove operations with `O(1)` cost are guaranteed. Removing an item is on average `O(1)`. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#immutable-queues "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#immutable-queues "Scala's Collection Library overview"]] * section on `Immutable Queues` for more information. * * @define Coll `immutable.Queue` diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala index a0f0e8692f97..a51c7b9e7bf6 100644 --- a/src/library/scala/collection/immutable/TreeMap.scala +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -60,7 +60,7 @@ import scala.runtime.AbstractFunction2 * @tparam V the type of the values associated with the keys. * @param ordering the implicit ordering used to compare objects of type `A`. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] * section on `Red-Black Trees` for more information. * * @define Coll immutable.TreeMap diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index e51479ae657b..f0be91b72acc 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -26,7 +26,7 @@ import scala.runtime.AbstractFunction1 * @tparam A the type of the elements contained in this tree set * @param ordering the implicit ordering used to compare objects of type `A` * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#red-black-trees "Scala's Collection Library overview"]] * section on `Red-Black Trees` for more information. * * @define Coll `immutable.TreeSet` diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala index e9dfec3455cd..e3ddeb71ef8e 100644 --- a/src/library/scala/collection/mutable/ArrayBuffer.scala +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -26,7 +26,7 @@ import scala.collection.generic.DefaultSerializable * access take constant time (amortized time). Prepends and removes are * linear in the buffer size. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#array-buffers "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#array-buffers "Scala's Collection Library overview"]] * section on `Array Buffers` for more information. * diff --git a/src/library/scala/collection/mutable/BitSet.scala b/src/library/scala/collection/mutable/BitSet.scala index cf727972f0f3..69ecc122c1f9 100644 --- a/src/library/scala/collection/mutable/BitSet.scala +++ b/src/library/scala/collection/mutable/BitSet.scala @@ -23,7 +23,7 @@ import scala.annotation.implicitNotFound * * $bitsetinfo * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#mutable-bitsets "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#mutable-bitsets "Scala's Collection Library overview"]] * section on `Mutable Bitsets` for more information. * * @define Coll `BitSet` diff --git a/src/library/scala/collection/mutable/CollisionProofHashMap.scala b/src/library/scala/collection/mutable/CollisionProofHashMap.scala index f7619cd1384f..8542b5b56a01 100644 --- a/src/library/scala/collection/mutable/CollisionProofHashMap.scala +++ b/src/library/scala/collection/mutable/CollisionProofHashMap.scala @@ -24,7 +24,7 @@ import scala.runtime.Statics * as determined by the `Ordering` has to be consistent with `equals` and `hashCode`. Universal equality * of numeric types is not supported (similar to `AnyRefMap`). * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] * section on `Hash Tables` for more information. * * @define Coll `mutable.CollisionProofHashMap` diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala index 160bbfb79d56..7ad3cf3869e8 100644 --- a/src/library/scala/collection/mutable/HashMap.scala +++ b/src/library/scala/collection/mutable/HashMap.scala @@ -20,7 +20,7 @@ import scala.util.hashing.MurmurHash3 /** This class implements mutable maps using a hashtable. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] * section on `Hash Tables` for more information. * * @tparam K the type of the keys contained in this hash map. diff --git a/src/library/scala/collection/mutable/HashSet.scala b/src/library/scala/collection/mutable/HashSet.scala index 49be7fa4e9e2..425721a41626 100644 --- a/src/library/scala/collection/mutable/HashSet.scala +++ b/src/library/scala/collection/mutable/HashSet.scala @@ -20,7 +20,7 @@ import scala.util.hashing.MurmurHash3 /** This class implements mutable sets using a hashtable. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#hash-tables "Scala's Collection Library overview"]] * section on `Hash Tables` for more information. * * @define Coll `mutable.HashSet` diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index 15cba2e663db..d66525763163 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -23,7 +23,7 @@ import scala.runtime.Statics.releaseFence /** A `Buffer` implementation backed by a list. It provides constant time * prepend and append. Most other operations are linear. * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#list-buffers "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#list-buffers "Scala's Collection Library overview"]] * section on `List Buffers` for more information. * * @tparam A the type of this list buffer's elements. diff --git a/src/library/scala/collection/mutable/StringBuilder.scala b/src/library/scala/collection/mutable/StringBuilder.scala index d8b5966158f2..1d8b9563e917 100644 --- a/src/library/scala/collection/mutable/StringBuilder.scala +++ b/src/library/scala/collection/mutable/StringBuilder.scala @@ -42,7 +42,7 @@ import scala.Predef.{ // unimport char-related implicit conversions to avoid tri * * $multipleResults * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#stringbuilders "Scala's Collection Library overview"]] * section on `StringBuilders` for more information. * * @define Coll `mutable.IndexedSeq` diff --git a/src/library/scala/collection/mutable/WeakHashMap.scala b/src/library/scala/collection/mutable/WeakHashMap.scala index 7f08253a106a..7286a318e1f9 100644 --- a/src/library/scala/collection/mutable/WeakHashMap.scala +++ b/src/library/scala/collection/mutable/WeakHashMap.scala @@ -24,7 +24,7 @@ import scala.collection.convert.JavaCollectionWrappers.{JMapWrapper, JMapWrapper * @tparam K type of keys contained in this map * @tparam V type of values associated with the keys * - * @see [[https://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#weak-hash-maps "Scala's Collection Library overview"]] + * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-mutable-collection-classes.html#weak-hash-maps "Scala's Collection Library overview"]] * section on `Weak Hash Maps` for more information. * * @define Coll `WeakHashMap` From 9f46827e57bbb2bea8650620de37bd0d0a8b2888 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 5 Feb 2023 10:25:53 -0800 Subject: [PATCH 151/261] Improve message for implicit object --- .../scala/tools/nsc/typechecker/Implicits.scala | 2 +- test/files/neg/t2206.check | 2 +- test/files/neg/t5197.check | 7 +++++++ test/files/neg/t5197.scala | 14 ++++++++++++++ test/files/neg/t7131.check | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/t5197.check create mode 100644 test/files/neg/t5197.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 71abc0a846df..3f937ecdadb8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1265,7 +1265,7 @@ trait Implicits extends splain.SplainData { if (invalidImplicits.nonEmpty) setAddendum(pos, () => - s"\n Note: implicit ${invalidImplicits.head} is not applicable here because it comes after the application point and it lacks an explicit result type" + s"\n Note: implicit ${invalidImplicits.head} is not applicable here because it comes after the application point and it lacks an explicit result type.${if (invalidImplicits.head.isModule) " An object can be written as a lazy val with an explicit type." else ""}" ) } diff --git a/test/files/neg/t2206.check b/test/files/neg/t2206.check index 75c5e19c58e5..9aca6ff9f50a 100644 --- a/test/files/neg/t2206.check +++ b/test/files/neg/t2206.check @@ -1,5 +1,5 @@ t2206.scala:10: error: value f is not a member of o.A - Note: implicit method ax is not applicable here because it comes after the application point and it lacks an explicit result type + Note: implicit method ax is not applicable here because it comes after the application point and it lacks an explicit result type. a.f() ^ t2206.scala:13: warning: Implicit definition should have explicit type (inferred o.AX) diff --git a/test/files/neg/t5197.check b/test/files/neg/t5197.check new file mode 100644 index 000000000000..2d1d16a12cc0 --- /dev/null +++ b/test/files/neg/t5197.check @@ -0,0 +1,7 @@ +t5197.scala:12: error: type mismatch; + found : List[String] + required: Int + Note: implicit object O2 is not applicable here because it comes after the application point and it lacks an explicit result type. An object can be written as a lazy val with an explicit type. + val y: Int = List("b") + ^ +1 error diff --git a/test/files/neg/t5197.scala b/test/files/neg/t5197.scala new file mode 100644 index 000000000000..8e568ea47660 --- /dev/null +++ b/test/files/neg/t5197.scala @@ -0,0 +1,14 @@ + +// scalac: -Werror -feature + +// Periodic reminder that the feature is not required for implicit function values. +//import scala.language.implicitConversions + +object A { + val x: Int = List("a") + implicit lazy val O1: (List[String] => Int) = _.head.toInt +} +object B { + val y: Int = List("b") + implicit object O2 extends (List[String] => Int) { def apply(ss: List[String]): Int = ss.head.toInt } +} diff --git a/test/files/neg/t7131.check b/test/files/neg/t7131.check index 86df78172b9c..2c8a35025cf3 100644 --- a/test/files/neg/t7131.check +++ b/test/files/neg/t7131.check @@ -1,7 +1,7 @@ t7131.scala:21: error: type mismatch; found : Iterable[U] required: That - Note: implicit method convertToSimpleMappable is not applicable here because it comes after the application point and it lacks an explicit result type + Note: implicit method convertToSimpleMappable is not applicable here because it comes after the application point and it lacks an explicit result type. x.value.map(f) ^ t7131.scala:28: warning: Implicit definition should have explicit type (inferred ObservableValue.TraversableMappable[T,Container]) From 779f5d5c541fa52516942581955221433cab3e3e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 5 Feb 2023 11:26:00 -0800 Subject: [PATCH 152/261] Test for conversion before eta expansion --- test/files/pos/t3218.scala | 41 +++++++++++++++++++++++++++++++++++++ test/files/pos/t3218b.scala | 35 +++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/files/pos/t3218.scala create mode 100644 test/files/pos/t3218b.scala diff --git a/test/files/pos/t3218.scala b/test/files/pos/t3218.scala new file mode 100644 index 000000000000..cbf9277dfdbe --- /dev/null +++ b/test/files/pos/t3218.scala @@ -0,0 +1,41 @@ + +import java.util.{List => JList} +import scala.collection.mutable +import scala.collection.JavaConverters.asScalaBuffer +import scala.language.existentials +import scala.language.implicitConversions + +object Test extends App { + + def fromJava[T](li: JList[T]): List[T] = asScalaBuffer(li).toList + + implicit def `list asScalaBuffer`[A](l: JList[A]): mutable.Buffer[A] = asScalaBuffer(l) + + // implicit conversion - ok + def test1(jList:JList[_]) = jList.filter(_.isInstanceOf[java.lang.Object]) + + // explicit conversion - ok + def test2(jList:JList[_]) = { + val f = fromJava(jList).filter _ + f(_.isInstanceOf[java.lang.Object]) + } + + // implicit conversion - error + def test3(jList:JList[_]) = { + val f = jList.filter _ + f(_.isInstanceOf[java.lang.Object]) + } + + val ss: JList[String] = { + val res = new java.util.ArrayList[String] + res.add("hello, world") + res + } + + println {( + test1(ss), + test2(ss), + test3(ss), + )} + +} diff --git a/test/files/pos/t3218b.scala b/test/files/pos/t3218b.scala new file mode 100644 index 000000000000..d9c6b8e6ecea --- /dev/null +++ b/test/files/pos/t3218b.scala @@ -0,0 +1,35 @@ + +import language.implicitConversions + +trait T +trait U { + def u(x: Any) = x.toString * 2 +} +class C { + implicit def cv(t: T): U = new U {} + def f(t: T): Any => String = cv(t).u _ + def g(t: T): Any => String = t.u _ + def h(t: T) = t.u _ +} + +object Test extends App { + val c = new C + val t = new T {} + println {( + c.f(t)("f"), + c.g(t)("g"), + c.h(t)("h"), + )} +} + +/* +2.11, 2.12 say: +t3218b.scala:11: error: _ must follow method; cannot follow Any => String + def g(t: T): Any => String = t.u _ + ^ +t3218b.scala:12: error: missing argument list for method u in trait U +Unapplied methods are only converted to functions when a function type is expected. +You can make this conversion explicit by writing `u _` or `u(_)` instead of `u`. + def h(t: T) = t.u _ + ^ +*/ From c7a16c918f10fe52ce5ee5d08b8ef469b16c7f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Costa?= Date: Sun, 5 Feb 2023 22:27:26 +0100 Subject: [PATCH 153/261] Replace removeFirst with removeHead in ArrayDeque documentation --- src/library/scala/collection/mutable/ArrayDeque.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/scala/collection/mutable/ArrayDeque.scala b/src/library/scala/collection/mutable/ArrayDeque.scala index 5dc4cbbaf83f..a61b6c94d973 100644 --- a/src/library/scala/collection/mutable/ArrayDeque.scala +++ b/src/library/scala/collection/mutable/ArrayDeque.scala @@ -21,7 +21,7 @@ import scala.reflect.ClassTag /** An implementation of a double-ended queue that internally uses a resizable circular buffer. * - * Append, prepend, removeFirst, removeLast and random-access (indexed-lookup and indexed-replacement) + * Append, prepend, removeHead, removeLast and random-access (indexed-lookup and indexed-replacement) * take amortized constant time. In general, removals and insertions at i-th index are O(min(i, n-i)) * and thus insertions and removals from end/beginning are fast. * From b6a26e531306b78984e20b1a64a434a0de5304e7 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 6 Feb 2023 14:19:43 +0100 Subject: [PATCH 154/261] [backport] warn when inherited takes precedence over outer definitions Under `-Xsource:2.13`, warn when an inherited member takes precedence over an outer definition. --- .../tools/nsc/typechecker/Contexts.scala | 66 ++++++++++++++-- .../scala/tools/nsc/typechecker/Typers.scala | 2 + .../reflect/internal/StdAttachments.scala | 2 + .../reflect/runtime/JavaUniverseForce.scala | 1 + test/files/neg/t11921-alias.check | 31 ++++++++ test/files/neg/t11921-alias.scala | 70 ++++++++++++++++ test/files/neg/t11921b.check | 45 +++++++++++ test/files/neg/t11921b.scala | 79 +++++++++++++++++++ test/files/pos/t11921b.scala | 66 ++++++++++++++++ test/files/pos/t11921c.scala | 22 ++++++ 10 files changed, 379 insertions(+), 5 deletions(-) create mode 100644 test/files/neg/t11921-alias.check create mode 100644 test/files/neg/t11921-alias.scala create mode 100644 test/files/neg/t11921b.check create mode 100644 test/files/neg/t11921b.scala create mode 100644 test/files/pos/t11921b.scala create mode 100644 test/files/pos/t11921c.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 26a2f393f6f6..7e1fc568156c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -56,7 +56,27 @@ trait Contexts { self: Analyzer => LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - + def ambiguousWithEnclosing(sym: Symbol, other: Symbol, otherEnclClass: Symbol) = + if (!currentRun.isScala213) None else { + val enclDesc = if (otherEnclClass.isAnonymousClass) "anonymous class" else otherEnclClass.toString + val parent = otherEnclClass.parentSymbols.find(_.isNonBottomSubClass(other.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != other.owner) s", inherited through parent $parent" else "" + val message = + s"""it is both defined in the enclosing ${sym.owner} and available in the enclosing $enclDesc as $other (defined in ${other.ownsString}$inherit) + |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. + |To continue using the symbol from the superclass, write `this.${sym.name}`.""".stripMargin + if (currentRun.isScala3) + Some(LookupAmbiguous(message)) + else { + // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning + other.updateAttachment( + LookupAmbiguityWarning( + s"""reference to ${sym.name} is ambiguous; + |$message + |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) + None + } + } private lazy val startContext = NoContext.make( Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, @@ -1228,16 +1248,48 @@ trait Contexts { self: Analyzer => return finishDefSym(constructorSym, cx.enclClass.prefix) } + var foundInSuper: Boolean = false + var outerDefSym: Symbol = NoSymbol + // cx.scope eq null arises during FixInvalidSyms in Duplicators while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) { pre = cx.enclClass.prefix defSym = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope) match { - case NoSymbol => searchPrefix - case found => found + case NoSymbol => + val prefixSym = searchPrefix + if (currentRun.isScala213 && prefixSym.exists && prefixSym.owner != cx.owner) + foundInSuper = true + prefixSym + case found => + found } if (!defSym.exists) cx = cx.outer // push further outward } + + val defPre = pre + val defCx = cx + + while (foundInSuper && (cx ne NoContext) && (cx.scope ne null)) { + pre = cx.enclClass.prefix + val next = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope).orElse(searchPrefix) + if (next.exists && next.owner == cx.owner && thisContext.unit.exists && next.sourceFile == thisContext.unit.source.file) { + outerDefSym = next + cx = NoContext + } else + cx = cx.outer + } + if (outerDefSym.exists) { + if ((defSym.isAliasType || outerDefSym.isAliasType) && defPre.memberType(defSym) =:= pre.memberType(outerDefSym)) + outerDefSym = NoSymbol + if (defSym.isStable && outerDefSym.isStable && + (pre.memberType(outerDefSym).termSymbol == defSym || defPre.memberType(defSym).termSymbol == outerDefSym)) + outerDefSym = NoSymbol + } + + pre = defPre + cx = defCx + if (symbolDepth < 0) symbolDepth = cx.depth @@ -1305,8 +1357,12 @@ trait Contexts { self: Analyzer => } // At this point only one or the other of defSym and impSym might be set. - if (defSym.exists) finishDefSym(defSym, pre) - else if (impSym.exists) { + if (defSym.exists) { + val ambiguity = + if (outerDefSym.exists) ambiguousWithEnclosing(outerDefSym, defSym, cx.enclClass.owner) + else None + ambiguity.getOrElse(finishDefSym(defSym, pre)) + } else if (impSym.exists) { // If we find a competitor imp2 which imports the same name, possible outcomes are: // // - same depth, imp1 wild, imp2 explicit: imp2 wins, drop imp1 diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cc320a1f904a..3e9210da1343 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5275,6 +5275,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case sym => typed1(tree setSymbol sym, mode, pt) } case LookupSucceeded(qual, sym) => + sym.getAndRemoveAttachment[LookupAmbiguityWarning].foreach(w => + runReporting.warning(tree.pos, w.msg, WarningCategory.Other, context.owner)) (// this -> Foo.this if (sym.isThisSym) typed1(This(sym.owner) setPos tree.pos, mode, pt) diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index cf9bc009345e..393f586af3d1 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -131,4 +131,6 @@ trait StdAttachments { // When typing a Def with this attachment, change the owner of its RHS from origalOwner to the symbol of the Def case class ChangeOwnerAttachment(originalOwner: Symbol) + + case class LookupAmbiguityWarning(msg: String) extends PlainAttachment } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index e05bc5dcc4d5..7819c389ecda 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -68,6 +68,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.TypeParamVarargsAttachment this.KnownDirectSubclassesCalled this.ChangeOwnerAttachment + this.LookupAmbiguityWarning this.noPrint this.typeDebug // inaccessible: this.posAssigner diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check new file mode 100644 index 000000000000..241efa7decd3 --- /dev/null +++ b/test/files/neg/t11921-alias.check @@ -0,0 +1,31 @@ +t11921-alias.scala:18: warning: reference to TT is ambiguous; +it is both defined in the enclosing object O and available in the enclosing class D as type TT (defined in class C) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.TT`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def n(x: TT) = x // ambiguous + ^ +t11921-alias.scala:38: warning: reference to c is ambiguous; +it is both defined in the enclosing class B and available in the enclosing anonymous class as value c (defined in class A) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.c`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def n = c // ambiguous + ^ +t11921-alias.scala:57: warning: reference to name is ambiguous; +it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class C) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.name`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(name) + ^ +t11921-alias.scala:67: warning: reference to name is ambiguous; +it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.name`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(name) + ^ +error: No warnings can be incurred under -Xfatal-warnings. +four warnings found +one error found diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala new file mode 100644 index 000000000000..ceccd9745ca6 --- /dev/null +++ b/test/files/neg/t11921-alias.scala @@ -0,0 +1,70 @@ +// scalac: -Xfatal-warnings -Xsource:2.13 + +object t1 { + class C[T] { type TT = T } + object O { + type TT = String + class D extends C[TT] { + def n(x: TT) = x // OK + } + } +} + +object t2 { + class C[T] { type TT <: T } + object O { + type TT = String + class D extends C[TT] { + def n(x: TT) = x // ambiguous + } + } +} + +object t3 { + trait Context + class A[C <: Context](val c: C) + class B(val c: Context) { b => + val a = new A[c.type](c) { + def n = c // OK + } + } +} + +object t4 { + trait Context + class A[C <: Context](val c: C) + class B(val c: Context) { b => + val a = new A(c) { + def n = c // ambiguous + } + } +} + +object t5 { + trait TT + class K[T <: TT](val t: T) + class C { + def f(t: TT) = new K[t.type](t) { + def test = t + } + } +} + +object t6 { + class C(val name: String) + object Test { + def m(name: String) = new C(name) { + println(name) + } + } +} + +object t7 { + abstract class A(val name: String) + class C(name: String) extends A(name) + object Test { + def m(name: String) = new C(name) { + println(name) + } + } +} diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check new file mode 100644 index 000000000000..3d47ac28c834 --- /dev/null +++ b/test/files/neg/t11921b.check @@ -0,0 +1,45 @@ +t11921b.scala:11: warning: reference to x is ambiguous; +it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(x) // error + ^ +t11921b.scala:15: warning: reference to x is ambiguous; +it is both defined in the enclosing object Test and available in the enclosing anonymous class as value x (defined in class C) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(x) // error + ^ +t11921b.scala:26: warning: reference to y is ambiguous; +it is both defined in the enclosing method c and available in the enclosing anonymous class as value y (defined in class D) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.y`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(y) // error + ^ +t11921b.scala:38: warning: reference to y is ambiguous; +it is both defined in the enclosing method c and available in the enclosing class E as value y (defined in class D) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.y`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(y) // error + ^ +t11921b.scala:65: warning: reference to global is ambiguous; +it is both defined in the enclosing package and available in the enclosing object D as value global (defined in class C) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.global`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(global) // error + ^ +t11921b.scala:75: warning: reference to x is ambiguous; +it is both defined in the enclosing object Uhu and available in the enclosing class C as value x (defined in class A, inherited through parent class B) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.x`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def t = x // ambiguous, message mentions parent B + ^ +error: No warnings can be incurred under -Xfatal-warnings. +6 warnings found +one error found diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala new file mode 100644 index 000000000000..c6218948901d --- /dev/null +++ b/test/files/neg/t11921b.scala @@ -0,0 +1,79 @@ +// scalac: -Xfatal-warnings -Xsource:2.13 + +object test1 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + println(x) // error + } + def f() = + new C { + println(x) // error + } + } +} + +object test2 { + def c(y: Float) = { + class D { + val y = 2 + } + new D { + println(y) // error + } + } +} + +object test3 { + def c(y: Float) = { + class D { + val y = 2 + } + class E extends D { + class F { + println(y) // error + } + } + } +} + +object test4 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + def x(y: Int) = 3 + val y: Int = this.x // OK + val z: Int = x // OK + } + } +} + +object global + +class C { + val global = 42 +} +object D extends C { + println(global) // error +} + +object test5 { + class A { val x = 1 } + class B extends A + object Uhu { + val x = 2 + class C extends B { + class Inner { + def t = x // ambiguous, message mentions parent B + } + } + } +} diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala new file mode 100644 index 000000000000..f7407e7f8528 --- /dev/null +++ b/test/files/pos/t11921b.scala @@ -0,0 +1,66 @@ +// scalac: -Xfatal-warnings -Xsource:2.13 -Wconf:msg=legacy-binding:s + +object test1 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + println(x) // error + } + def f() = + new C { + println(x) // error + } + } +} + +object test2 { + def c(y: Float) = { + class D { + val y = 2 + } + new D { + println(y) // error + } + } +} + +object test3 { + def c(y: Float) = { + class D { + val y = 2 + } + class E extends D { + class F { + println(y) // error + } + } + } +} + +object test4 { + + class C { + val x = 0 + } + object Test { + val x = 1 + class D extends C { + def x(y: Int) = 3 + val y: Int = this.x // OK + val z: Int = x // OK + } + } +} + +object global + +class C { + val global = 42 +} +object D extends C { + println(global) // error +} diff --git a/test/files/pos/t11921c.scala b/test/files/pos/t11921c.scala new file mode 100644 index 000000000000..9afe4335975f --- /dev/null +++ b/test/files/pos/t11921c.scala @@ -0,0 +1,22 @@ +// scalac: -Xfatal-warnings -Xsource:2.13 + +package test.templates { + object `package` { + type String = java.lang.String + val String = new StringCompanion + class StringCompanion { def boo = ??? } + } + + trait Base { + type String = test.templates.String + type T <: Foo + val T: FooExtractor + trait Foo { def foo: Int } + trait FooExtractor { def apply(foo: Int): Unit; def unapply(t: Foo): Option[Int] } + } + + trait Api extends Base { + override type T <: FooApi + trait FooApi extends Foo { def bar: String } + } +} From e23cc46d8ab6d4103beea87833a2bc0a11476776 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 16 Dec 2022 11:05:50 +0100 Subject: [PATCH 155/261] [backport] Don't GLB binders of type patterns, use the type directly In type patterns `c match { case x: T }`, the translation would assign the GLB of `c`'s type and `T` to the varaible `x`. This seems to trace back to the first version of the "virtual pattern matcher". I could not find a similar use of `glb` in that revision of the codebase. So I'm not sure if it was a new addition, or picked up from the previous implementation. https://github.com/scala/scala/blob/8a9fd64129926eea35f7dca181242855f14e153f/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala#L438-L440 In the test case, the GLB collapsed to `Null` because its computation failed (combination of f-bounds, existentials, skolems that I don't follow), see `throw GlbFailure`. This is how it's been for 14 years (b894f804ad). This resulted in a cast to `Null$` failing at runtime. I assume GLB is fixed in Scala 3, as this core of the type system has a new implementation. But the test case as such doesn't compile in Scala 3 due to the non-wildcard existential. --- .../transform/patmat/MatchTranslation.scala | 2 +- test/files/run/t12702.check | 2 ++ test/files/run/t12702.scala | 19 ++++++++++++ .../tools/nsc/symtab/SymbolTableTest.scala | 29 +++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t12702.check create mode 100644 test/files/run/t12702.scala diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index fa19f67136a3..f18dc348c973 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -75,7 +75,7 @@ trait MatchTranslation { case TypeBound(tpe) => tpe case tree => tree.tpe } - def glbWith(other: Type) = glb(tpe :: other :: Nil).normalize + def glbWith(other: Type) = if (currentRun.isScala213) other else glb(tpe :: other :: Nil).normalize object SymbolAndTypeBound { def unapply(tree: Tree): Option[(Symbol, Type)] = tree match { diff --git a/test/files/run/t12702.check b/test/files/run/t12702.check new file mode 100644 index 000000000000..42be293d06cf --- /dev/null +++ b/test/files/run/t12702.check @@ -0,0 +1,2 @@ +warning: one feature warning; re-run with -feature for details +IOS diff --git a/test/files/run/t12702.scala b/test/files/run/t12702.scala new file mode 100644 index 000000000000..0010e897d327 --- /dev/null +++ b/test/files/run/t12702.scala @@ -0,0 +1,19 @@ +// scalac: -Xsource:2.13 + +object Test { + trait MFSS[X <: MFSS[_]] + trait CS extends MFSS[CS] + trait MFI { type ST } + case class MFSD[S](mFI: MFI {type ST = S}) + case object IOS extends MFI { type ST = CS } + type SD = MFSD[S] forSome { + type S <: MFSS[S] + } + def bad(sd: SD) = sd.mFI match { + case ios: IOS.type => println(ios) + } + def main(args: Array[String]): Unit = { + val x = MFSD(IOS) + bad(x) + } +} diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala index 5a921a5edad7..3144a6f7a1dd 100644 --- a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala @@ -49,4 +49,33 @@ class SymbolTableTest { import symbolTable._ assert(NoSymbol.outerClass == NoSymbol) } + + @Test def t12702_glb(): Unit = { + import symbolTable._ + import SymbolTableTest.t12702._ + val t1 = typeOf[IOS.type] + val t2 = { + val sd = typeOf[SD] + sd.memberType(sd.member(TermName("mFI"))).finalResultType + } + // t1: Test.IOS.type + // t2: Test.MFI{type +ST = S} + + // Ends up in `throw GlbFailure` in glb => Null + assertTrue(definitions.NullTpe =:= glb(t1 :: t2 :: Nil)) + } +} + +object SymbolTableTest { + object t12702 { + import scala.language.existentials + trait MFSS[X <: MFSS[_]] + trait CS extends MFSS[CS] + trait MFI { type ST } + case class MFSD[S](mFI: MFI {type ST = S}) + case object IOS extends MFI { type ST = CS } + type SD = MFSD[S] forSome { + type S <: MFSS[S] + } + } } From a33835e20591cd730bd33ca061753816d506efdd Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 7 Feb 2023 17:39:07 +0100 Subject: [PATCH 156/261] test APPLYsigpoly, fail in forced annotations --- .../scala/tools/nsc/tasty/TreeUnpickler.scala | 6 +++++ .../scala/tools/tasty/TastyFormat.scala | 2 ++ test/tasty/neg/src-2/TestApplySigPoly.check | 4 +++ .../neg/src-2/TestApplySigPoly_fail.scala | 8 ++++++ .../neg/src-2/TestApplySigPoly_pre.scala | 25 +++++++++++++++++++ test/tasty/neg/src-3/ApplySigPoly.scala | 20 +++++++++++++++ .../src-2/tastytest/TestApplySigPoly.scala | 14 +++++++++++ .../run/src-3/tastytest/ApplySigPoly.scala | 25 +++++++++++++++++++ 8 files changed, 104 insertions(+) create mode 100644 test/tasty/neg/src-2/TestApplySigPoly.check create mode 100644 test/tasty/neg/src-2/TestApplySigPoly_fail.scala create mode 100644 test/tasty/neg/src-2/TestApplySigPoly_pre.scala create mode 100644 test/tasty/neg/src-3/ApplySigPoly.scala create mode 100644 test/tasty/run/src-2/tastytest/TestApplySigPoly.scala create mode 100644 test/tasty/run/src-3/tastytest/ApplySigPoly.scala diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 864c4cedd5fc..6d38ff105bad 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -1148,6 +1148,9 @@ class TreeUnpickler[Tasty <: TastyUniverse]( tpd.Apply(fn, until(end)(readTerm()(argsCtx))) } case TYPEAPPLY => tpd.TypeApply(readTerm(), until(end)(readTpt())) + case APPLYsigpoly => + // this is skipped if it appears in parents, so only affects forced annotation trees + signaturePolymorphicIsUnsupported case TYPED => tpd.Typed(readTerm(), readTpt()) case IF => if (nextByte === INLINE) unsupportedTermTreeError("inline conditional expression") @@ -1241,6 +1244,9 @@ class TreeUnpickler[Tasty <: TastyUniverse]( */ private def abortMacroHole[T]: T = abortWith(msg = "Scala 3 macro hole in pickled TASTy") + private def signaturePolymorphicIsUnsupported[T](implicit ctx: Context): T = + unsupportedTermTreeError("signature polymorphic application") + private def metaprogrammingIsUnsupported[T](implicit ctx: Context): T = unsupportedError("Scala 3 metaprogramming features") diff --git a/src/compiler/scala/tools/tasty/TastyFormat.scala b/src/compiler/scala/tools/tasty/TastyFormat.scala index 0c06b899d389..8412767fdfa9 100644 --- a/src/compiler/scala/tools/tasty/TastyFormat.scala +++ b/src/compiler/scala/tools/tasty/TastyFormat.scala @@ -325,6 +325,7 @@ object TastyFormat { // final val ??? = 178 // final val ??? = 179 final val METHODtype = 180 + final val APPLYsigpoly = 181 final val MATCHtype = 190 final val MATCHtpt = 191 @@ -491,6 +492,7 @@ object TastyFormat { case BOUNDED => "BOUNDED" case APPLY => "APPLY" case TYPEAPPLY => "TYPEAPPLY" + case APPLYsigpoly => "APPLYsigpoly" case NEW => "NEW" case THROW => "THROW" case TYPED => "TYPED" diff --git a/test/tasty/neg/src-2/TestApplySigPoly.check b/test/tasty/neg/src-2/TestApplySigPoly.check new file mode 100644 index 000000000000..ccfb756803d9 --- /dev/null +++ b/test/tasty/neg/src-2/TestApplySigPoly.check @@ -0,0 +1,4 @@ +TestApplySigPoly_fail.scala:6: error: Unsupported Scala 3 signature polymorphic application in an annotation of method annotatedMethod; note that complex trees are not yet supported for Annotations; found in method annotatedMethod in object tastytest.ApplySigPoly. + def test = TestApplySigPolyPre.forceAnnots[ApplySigPoly.type, ApplySigPoly.boxAnnot] + ^ +1 error diff --git a/test/tasty/neg/src-2/TestApplySigPoly_fail.scala b/test/tasty/neg/src-2/TestApplySigPoly_fail.scala new file mode 100644 index 000000000000..d86094e4d4b1 --- /dev/null +++ b/test/tasty/neg/src-2/TestApplySigPoly_fail.scala @@ -0,0 +1,8 @@ +package tastytest + +object TestApplySigPoly { + + // force an annotation tree with an APPLYsigpoly node + def test = TestApplySigPolyPre.forceAnnots[ApplySigPoly.type, ApplySigPoly.boxAnnot] + +} diff --git a/test/tasty/neg/src-2/TestApplySigPoly_pre.scala b/test/tasty/neg/src-2/TestApplySigPoly_pre.scala new file mode 100644 index 000000000000..dc7d49bca384 --- /dev/null +++ b/test/tasty/neg/src-2/TestApplySigPoly_pre.scala @@ -0,0 +1,25 @@ +package tastytest + +import scala.language.experimental.macros + +import scala.reflect.macros.blackbox.Context + +object TestApplySigPolyPre { + + /** forces annotations of type `A` on methods from class `T` */ + def forceAnnots[T, A]: Unit = macro Macros.forceAnnotsImpl[T, A] + + object Macros { + def forceAnnotsImpl[T, A](c: Context)(implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A]): c.Expr[Unit] = { + import c.universe._ + for { + method <- weakTypeOf[T].members.filter(_.isMethod) + annot <- method.annotations.find(_.tree.tpe =:= weakTypeOf[A]) + } { + annot.tree + } + c.Expr[Unit](q"()") + } + } + +} diff --git a/test/tasty/neg/src-3/ApplySigPoly.scala b/test/tasty/neg/src-3/ApplySigPoly.scala new file mode 100644 index 000000000000..046da9ff69a2 --- /dev/null +++ b/test/tasty/neg/src-3/ApplySigPoly.scala @@ -0,0 +1,20 @@ +package tastytest + +object ApplySigPoly { + + class Foo { + def foo(x: Int): Int = x + } + + private val lookup = java.lang.invoke.MethodHandles.lookup() + private val mt = java.lang.invoke.MethodType.methodType(classOf[Int], classOf[Int]); + + val mh = lookup.findVirtual(classOf[Foo], "foo", mt) + + val self = new Foo() + + class boxAnnot(val value: Int) extends scala.annotation.Annotation + + @boxAnnot(mh.invokeExact(self, 23): Int) + def annotatedMethod: Int = 23 +} diff --git a/test/tasty/run/src-2/tastytest/TestApplySigPoly.scala b/test/tasty/run/src-2/tastytest/TestApplySigPoly.scala new file mode 100644 index 000000000000..b77464610b05 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestApplySigPoly.scala @@ -0,0 +1,14 @@ +package tastytest + +object TestApplySigPoly extends Suite("TestApplySigPoly") { + + test("construct class with APPLYsigpoly in parent") { + val subbox = new ApplySigPoly.SubBox() + assert(subbox.value == 23) + } + + test("call method with APPLYsigpoly in annotation") { + assert(ApplySigPoly.annotatedMethod == 23) + } + +} diff --git a/test/tasty/run/src-3/tastytest/ApplySigPoly.scala b/test/tasty/run/src-3/tastytest/ApplySigPoly.scala new file mode 100644 index 000000000000..4cdb4a4855d2 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/ApplySigPoly.scala @@ -0,0 +1,25 @@ +package tastytest + +object ApplySigPoly { + + class Foo { + def foo(x: Int): Int = x + } + + private val lookup = java.lang.invoke.MethodHandles.lookup() + private val mt = java.lang.invoke.MethodType.methodType(classOf[Int], classOf[Int]); + + val mh = lookup.findVirtual(classOf[Foo], "foo", mt) + + class IntBox(val value: Int) + + val self = new Foo() + + // There should appear a APPLYsigpoly node in the parent of SubBox + class SubBox extends IntBox(mh.invokeExact(self, 23): Int) + + class boxAnnot(val value: Int) extends scala.annotation.Annotation + + @boxAnnot(mh.invokeExact(self, 23): Int) + def annotatedMethod: Int = 23 +} From ecc0026b537f5d6f5431ecef976aa3deda2b5a19 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Wed, 8 Feb 2023 19:44:05 +0000 Subject: [PATCH 157/261] Update jackson-module-scala to 2.14.2 in 2.12.x --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 72c0c6ae910d..c9adff9075e3 100644 --- a/build.sbt +++ b/build.sbt @@ -413,7 +413,7 @@ lazy val compilerOptionsExporter = Project("compilerOptionsExporter", file(".") .settings(disablePublishing) .settings( libraryDependencies ++= { - val jacksonVersion = "2.13.5" + val jacksonVersion = "2.14.2" Seq( "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion, From abff93cfeff81956659f3997adbdf16bb864f811 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Wed, 8 Feb 2023 19:44:45 +0000 Subject: [PATCH 158/261] Update sbt-jmh to 0.4.4 in 2.12.x --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index d38cbaf10ebe..b45494705a5d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -32,4 +32,4 @@ concurrentRestrictions in Global := Seq( addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.4") From c0810555f5feb87c8efca537b78cdd9426a4e1ba Mon Sep 17 00:00:00 2001 From: ashish Date: Thu, 9 Feb 2023 14:56:18 +0530 Subject: [PATCH 159/261] small code improvement --- project/DottySupport.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index 84217ec18d85..ae9a5dbabdfc 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -68,10 +68,8 @@ object DottySupport { Compile / sourceGenerators += Def.task { object DottyLibrarySourceFilter extends FileFilter { def accept(file: File): Boolean = { - val name = file.name - val path = file.getCanonicalPath - file.isFile && - (path.endsWith(".scala") || path.endsWith(".java")) + val name = file.getName + file.isFile && (name.endsWith(".scala") || name.endsWith(".java")) } } From da4fe7cecd37244593b1499e6d8d626e724462a2 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 9 Feb 2023 18:58:38 -0800 Subject: [PATCH 160/261] GitHub Actions: update actions/checkout to v3 at e.g. https://github.com/scala/scala/actions/runs/4130256541 GitHub is telling us: > Node.js 12 actions are deprecated. Please update the following > actions to use Node.js 16: actions/checkout@v2. For more information > see: > https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/ I eyeballed https://github.com/actions/checkout/releases but didn't see any compatibility concerns --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ef580e00ae5..44967a1ccd31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: steps: - run: git config --global core.autocrlf false - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Java uses: actions/setup-java@v3 From 9f97fd16db91057523c8fe93595926b8690a0060 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 17 Jan 2023 10:45:31 -0800 Subject: [PATCH 161/261] Restore symbol gets deprecation directly --- .../symtab/classfile/ClassfileParser.scala | 1 + .../tools/partest/nest/DirectCompiler.scala | 2 +- test/files/jvm/deprecation.check | 30 +++++++++++++++++-- test/files/jvm/deprecation/Test_1.scala | 3 ++ test/files/jvm/deprecation/Use_2.java | 5 +++- test/files/neg/t10752.check | 5 +++- test/files/neg/t10752/Test_2.scala | 2 +- test/files/neg/t9617.check | 5 +++- test/files/neg/t9617/Test.scala | 4 +-- 9 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index ac20d4f16e0d..80d7285f9e56 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -826,6 +826,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { case tpnme.DeprecatedATTR => in.skip(attrLen) + sym.addAnnotation(JavaDeprecatedAttr) if (sym == clazz) staticModule.addAnnotation(JavaDeprecatedAttr) diff --git a/src/partest/scala/tools/partest/nest/DirectCompiler.scala b/src/partest/scala/tools/partest/nest/DirectCompiler.scala index f46e5bd53a80..4368bf6fd87a 100644 --- a/src/partest/scala/tools/partest/nest/DirectCompiler.scala +++ b/src/partest/scala/tools/partest/nest/DirectCompiler.scala @@ -122,7 +122,7 @@ class DirectCompiler(val runner: Runner) { if (command.files.nonEmpty) reportError(command.files.mkString("flags file may only contain compiler options, found: ", space, "")) } - suiteRunner.verbose(s"% compiling ${ sources.map(_.testIdent).mkString(space) }${ if (suiteRunner.debug) " -d " + outDir else ""}") + suiteRunner.verbose(sources.map(_.testIdent).mkString("% compiling ", space, if (suiteRunner.debug) s" -d $outDir" else "")) def execCompile() = if (command.shouldStopWithInfo) { diff --git a/test/files/jvm/deprecation.check b/test/files/jvm/deprecation.check index a135a2ef4bcb..2997495c7b9a 100644 --- a/test/files/jvm/deprecation.check +++ b/test/files/jvm/deprecation.check @@ -1,2 +1,28 @@ -Note: deprecation/Use_2.java uses or overrides a deprecated API. -Note: Recompile with -Xlint:deprecation for details. +Test_1.scala:7: warning: variable i in class Defs is deprecated + val u = d.i + 1 + ^ +Test_1.scala:8: warning: variable i in class Defs is deprecated + d.i = 2 + ^ +Test_1.scala:9: warning: method bar in class Defs is deprecated + val v = d.bar() + ^ +Test_1.scala:10: warning: class Inner in class Defs is deprecated + val i = new d.Inner + ^ +deprecation/Use_2.java:7: warning: [deprecation] Test.Inner in Test has been deprecated + Test.Inner a = u.new Inner(); + ^ +deprecation/Use_2.java:7: warning: [deprecation] Test.Inner in Test has been deprecated + Test.Inner a = u.new Inner(); + ^ +deprecation/Use_2.java:8: warning: [deprecation] f() in Test.Inner has been deprecated + int i = a.f(); + ^ +deprecation/Use_2.java:9: warning: [deprecation] g() in Test.Inner has been deprecated + int j = a.g(); + ^ +deprecation/Use_2.java:10: warning: [deprecation] g_$eq(int) in Test.Inner has been deprecated + a.g_$eq(5); + ^ +5 warnings diff --git a/test/files/jvm/deprecation/Test_1.scala b/test/files/jvm/deprecation/Test_1.scala index 26c636b4755b..04c9b22365be 100644 --- a/test/files/jvm/deprecation/Test_1.scala +++ b/test/files/jvm/deprecation/Test_1.scala @@ -1,3 +1,6 @@ + +// scalac: -Xlint:deprecation + class Test { def test: Unit = { val d = new Defs diff --git a/test/files/jvm/deprecation/Use_2.java b/test/files/jvm/deprecation/Use_2.java index 65da8a8fac90..36d5b4ffed9a 100644 --- a/test/files/jvm/deprecation/Use_2.java +++ b/test/files/jvm/deprecation/Use_2.java @@ -1,3 +1,6 @@ + +// javac: -Xlint:deprecation + class Use_2 { public int test() { Test u = new Test(); @@ -7,4 +10,4 @@ public int test() { a.g_$eq(5); return i + j; } -} \ No newline at end of file +} diff --git a/test/files/neg/t10752.check b/test/files/neg/t10752.check index 85f5c2072c17..76d046bf2408 100644 --- a/test/files/neg/t10752.check +++ b/test/files/neg/t10752.check @@ -1,3 +1,6 @@ +Test_2.scala:2: warning: class DeprecatedClass in package p1 is deprecated +object Test extends p1.DeprecatedClass { + ^ Test_2.scala:3: warning: class DeprecatedClass in package p1 is deprecated def useC = p1.DeprecatedClass.foo ^ @@ -5,5 +8,5 @@ Test_2.scala:4: warning: method foo in class DeprecatedMethod is deprecated def useM = p1.DeprecatedMethod.foo ^ error: No warnings can be incurred under -Werror. -2 warnings +3 warnings 1 error diff --git a/test/files/neg/t10752/Test_2.scala b/test/files/neg/t10752/Test_2.scala index 4193e24f5607..5f76ec601f72 100644 --- a/test/files/neg/t10752/Test_2.scala +++ b/test/files/neg/t10752/Test_2.scala @@ -1,5 +1,5 @@ // scalac: -Xlint:deprecation -Werror -object Test { +object Test extends p1.DeprecatedClass { def useC = p1.DeprecatedClass.foo def useM = p1.DeprecatedMethod.foo } diff --git a/test/files/neg/t9617.check b/test/files/neg/t9617.check index 20e7baa87a72..84a286a25bb7 100644 --- a/test/files/neg/t9617.check +++ b/test/files/neg/t9617.check @@ -1,3 +1,6 @@ +Test.scala:4: warning: class DeprecatedClass in package p1 is deprecated +object Test extends p1.DeprecatedClass { + ^ Test.scala:5: warning: class DeprecatedClass in package p1 is deprecated def useC = p1.DeprecatedClass.foo ^ @@ -5,5 +8,5 @@ Test.scala:6: warning: method foo in class DeprecatedMethod is deprecated def useM = p1.DeprecatedMethod.foo ^ error: No warnings can be incurred under -Werror. -2 warnings +3 warnings 1 error diff --git a/test/files/neg/t9617/Test.scala b/test/files/neg/t9617/Test.scala index b84cb229d090..efdd94f8a15d 100644 --- a/test/files/neg/t9617/Test.scala +++ b/test/files/neg/t9617/Test.scala @@ -1,7 +1,7 @@ -// scalac: -Xfatal-warnings -deprecation +// scalac: -Werror -Xlint:deprecation // Joint-compilation copy of test/files/neg/t10752/Test_2.scala -object Test { +object Test extends p1.DeprecatedClass { def useC = p1.DeprecatedClass.foo def useM = p1.DeprecatedMethod.foo } From 4d5849af3a9bd88342c8c88f82c31c71c8e0a18e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 10 Feb 2023 11:59:08 -0800 Subject: [PATCH 162/261] Prefer override protected, partest tweaks --- .../tools/nsc/transform/SpecializeTypes.scala | 2 +- .../tools/nsc/typechecker/Duplicators.scala | 2 +- .../scala/collection/mutable/ArrayDeque.scala | 2 +- .../scala/collection/mutable/Queue.scala | 2 +- .../scala/collection/mutable/Stack.scala | 2 +- .../collection/mutable/UnrolledBuffer.scala | 2 +- .../scala/tools/partest/nest/Runner.scala | 47 +++++++++---------- src/partest/scala/tools/partest/package.scala | 12 +++-- 8 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 8d4d9f8417c4..4eefd99bf219 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1423,7 +1423,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { override def enterSyntheticSym(tree: Tree): Symbol = tree.symbol } - protected override def newBodyDuplicator(context: Context): SpecializeBodyDuplicator = + override protected def newBodyDuplicator(context: Context): SpecializeBodyDuplicator = new SpecializeBodyDuplicator(context) override def newNamer(context: Context): Namer = diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 4d18d7b86953..4021cd9e4ba4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -58,7 +58,7 @@ abstract class Duplicators extends Analyzer { private var envSubstitution: SubstTypeMap = _ private class SubstSkolemsTypeMap(from: List[Symbol], to: List[Type]) extends SubstTypeMap(from, to) { - protected override def matches(sym1: Symbol, sym2: Symbol) = + override protected def matches(sym1: Symbol, sym2: Symbol) = if (sym2.isTypeSkolem) sym2.deSkolemize eq sym1 else sym1 eq sym2 } diff --git a/src/library/scala/collection/mutable/ArrayDeque.scala b/src/library/scala/collection/mutable/ArrayDeque.scala index 5dc4cbbaf83f..1169aaa137c1 100644 --- a/src/library/scala/collection/mutable/ArrayDeque.scala +++ b/src/library/scala/collection/mutable/ArrayDeque.scala @@ -434,7 +434,7 @@ class ArrayDeque[A] protected ( override def isEmpty = start == end - protected override def klone(): ArrayDeque[A] = new ArrayDeque(array.clone(), start = start, end = end) + override protected def klone(): ArrayDeque[A] = new ArrayDeque(array.clone(), start = start, end = end) override def iterableFactory: SeqFactory[ArrayDeque] = ArrayDeque diff --git a/src/library/scala/collection/mutable/Queue.scala b/src/library/scala/collection/mutable/Queue.scala index 43e5e15faba6..18cce0bd3852 100644 --- a/src/library/scala/collection/mutable/Queue.scala +++ b/src/library/scala/collection/mutable/Queue.scala @@ -110,7 +110,7 @@ class Queue[A] protected (array: Array[AnyRef], start: Int, end: Int) */ @`inline` final def front: A = head - protected override def klone(): Queue[A] = { + override protected def klone(): Queue[A] = { val bf = newSpecificBuilder bf ++= this bf.result() diff --git a/src/library/scala/collection/mutable/Stack.scala b/src/library/scala/collection/mutable/Stack.scala index fd711567a822..675666bc805c 100644 --- a/src/library/scala/collection/mutable/Stack.scala +++ b/src/library/scala/collection/mutable/Stack.scala @@ -114,7 +114,7 @@ class Stack[A] protected (array: Array[AnyRef], start: Int, end: Int) */ @`inline` final def top: A = head - protected override def klone(): Stack[A] = { + override protected def klone(): Stack[A] = { val bf = newSpecificBuilder bf ++= this bf.result() diff --git a/src/library/scala/collection/mutable/UnrolledBuffer.scala b/src/library/scala/collection/mutable/UnrolledBuffer.scala index 3799c7bced48..489f2a1b0387 100644 --- a/src/library/scala/collection/mutable/UnrolledBuffer.scala +++ b/src/library/scala/collection/mutable/UnrolledBuffer.scala @@ -438,5 +438,5 @@ object UnrolledBuffer extends StrictOptimizedClassTagSeqFactory[UnrolledBuffer] // Todo -- revisit whether inheritance is the best way to achieve this functionality private[collection] class DoublingUnrolledBuffer[T](implicit t: ClassTag[T]) extends UnrolledBuffer[T]()(t) { override def calcNextLength(sz: Int) = if (sz < 10000) sz * 2 else sz - protected override def newUnrolled = new UnrolledBuffer.Unrolled[T](0, new Array[T](4), null, this) + override protected def newUnrolled = new UnrolledBuffer.Unrolled[T](0, new Array[T](4), null, this) } diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 17e672203190..ec6bebfd2ad4 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -418,18 +418,15 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { * 2. Runs script function, providing log file and output directory as arguments. * 2b. or, just run the script without context and return a new context */ - def runInContext(body: => TestState): TestState = { - body - } + def runInContext(body: => TestState): TestState = body /** Grouped files in group order, and lex order within each group. */ - def groupedFiles(sources: List[File]): List[List[File]] = ( + def groupedFiles(sources: List[File]): List[List[File]] = if (sources.sizeIs > 1) { - val grouped = sources groupBy (_.group) - grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName)) + val grouped = sources.groupBy(_.group) + grouped.keys.toList.sorted.map(grouped(_).sortBy(_.getName)) } else List(sources) - ) /** Source files for the given test file. */ def sources(file: File): List[File] = if (file.isDirectory) file.listFiles.toList.filter(_.isJavaOrScala) else List(file) @@ -483,31 +480,31 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { files.flatMap(argsFor) } - abstract class CompileRound { - def fs: List[File] - def result: TestState + sealed abstract class CompileRound { + def files: List[File] def description: String + protected def computeResult: TestState + + final lazy val result: TestState = { pushTranscript(description); computeResult } - def fsString = fs map (_.toString stripPrefix parentFile.toString + "/") mkString " " - def isOk = result.isOk - def mkScalacString(): String = s"""scalac $fsString""" - override def toString = description + ( if (result.isOk) "" else "\n" + result.status ) + final protected def fsString = files.map(_.toString.stripPrefix(s"$parentFile/")).mkString(" ") + final override def toString = description + ( if (result.isOk) "" else "\n" + result.status ) } - case class OnlyJava(fs: List[File]) extends CompileRound { + final case class OnlyJava(files: List[File]) extends CompileRound { def description = s"""javac $fsString""" - lazy val result = { pushTranscript(description) ; javac(fs) } + override protected def computeResult = javac(files) } - case class OnlyScala(fs: List[File]) extends CompileRound { - def description = mkScalacString() - lazy val result = { pushTranscript(description) ; attemptCompile(fs) } + final case class OnlyScala(files: List[File]) extends CompileRound { + def description = s"""scalac $fsString""" + override protected def computeResult = attemptCompile(files) } - case class ScalaAndJava(fs: List[File]) extends CompileRound { - def description = mkScalacString() - lazy val result = { pushTranscript(description) ; attemptCompile(fs) } + final case class ScalaAndJava(files: List[File]) extends CompileRound { + def description = s"""scalac $fsString""" + override protected def computeResult = attemptCompile(files) } - case class SkipRound(fs: List[File], state: TestState) extends CompileRound { + final case class SkipRound(files: List[File], state: TestState) extends CompileRound { def description: String = state.status - lazy val result = { pushTranscript(description); state } + override protected def computeResult = state } def compilationRounds(file: File): List[CompileRound] = { @@ -535,7 +532,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = { - val (scalaFiles, javaFiles) = allFiles partition (_.isScala) + val (scalaFiles, javaFiles) = allFiles.partition(_.isScala) val round1 = if (scalaFiles.isEmpty) None else Some(ScalaAndJava(allFiles)) val round2 = if (javaFiles.isEmpty) None else Some(OnlyJava(javaFiles)) diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index ccddabe77feb..1f949bc8e303 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -58,6 +58,12 @@ package object partest { /** Sources have a numerical group, specified by name_7 and so on. */ private val GroupPattern = """.*_(\d+)""".r + private object IntOf { + def unapply(ds: String): Some[Int] = Some { + try ds.toInt + catch { case _: NumberFormatException => -1 } + } + } implicit class `special string ops`(private val s: String) extends AnyVal { def linesIfNonEmpty: Iterator[String] = if (!s.isEmpty) s.linesIterator else Iterator.empty @@ -85,11 +91,11 @@ package object partest { def hasExtension(ext: String) = sf hasExtension ext def changeExtension(ext: String): File = (sf changeExtension ext).jfile - /** The group number for this source file, or -1 for no group. */ + /** The group number for this source file, or -1 for no group or out of range. */ def group: Int = sf.stripExtension match { - case GroupPattern(g) if g.toInt >= 0 => g.toInt - case _ => -1 + case GroupPattern(IntOf(g)) => g + case _ => -1 } // Files.readString on jdk 11 From 91900fc4015388b532f208f15faa20b6f63f17cf Mon Sep 17 00:00:00 2001 From: kenji yoshida <6b656e6a69@gmail.com> Date: Sat, 11 Feb 2023 09:34:11 +0900 Subject: [PATCH 163/261] add dependabot.yml for GitHub Actions update --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..5ace4600a1f2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From d27098481ac3a6cf7bd0583b715890442f55ebc4 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 14 Feb 2023 12:58:15 -0800 Subject: [PATCH 164/261] spec: update Jekyll and Redcarpet versions --- .travis.yml | 2 +- Gemfile | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7da7ef852558..901279807904 100644 --- a/.travis.yml +++ b/.travis.yml @@ -134,7 +134,7 @@ jobs: - ruby -v - gem install bundler - bundler --version - - bundle install + - bundle install --path vendor/bundle # cribbed from https://github.com/SebastiaanKlippert/go-wkhtmltopdf/blob/master/.travis.yml - sudo apt-get update - sudo apt-get install -y build-essential xorg xfonts-75dpi libpng16-16 libssl1.1 diff --git a/Gemfile b/Gemfile index d37ec91782f6..b248ccc91835 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,10 @@ # To build the spec on Travis CI source "https://rubygems.org" -gem "jekyll", "3.6.3" +gem "jekyll", "3.9.3" gem "rouge" -# gem 's3_website' -gem "redcarpet", "3.5.1" +gem "redcarpet", "3.6.0" + +# we use redcarpet not kramdown, but current jekyll complains +# if this isn't present?! +gem 'kramdown-parser-gfm' From 405c93a5678f7d5612c0c5edf74f8869ffb4fed5 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 15 Feb 2023 10:39:51 +0100 Subject: [PATCH 165/261] Fix spec build --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index db6e68321df5..09cac9c1c93c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,8 @@ jobs: # bionic for newer ruby ("bundler requires Ruby version >= 2.6.0") dist: bionic language: ruby + # ruby 3.x is default, need to upgrade jekyll. using 2.7 for now. + rvm: 2.7 install: - ruby -v - gem install bundler From 334c1bb54a412c1a094a096eb0cb241dea0705a3 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 16 Feb 2023 14:19:58 +0100 Subject: [PATCH 166/261] [backport] Fixes to backport of new precedence warning This is a follow-up for PR 10297 that fixes false positive ambiguity warnings. --- .../tools/nsc/typechecker/Contexts.scala | 53 +++++++++++-------- test/files/pos/t11921b.scala | 34 +++++++++--- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 7e1fc568156c..2a9df72b351f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1267,32 +1267,39 @@ trait Contexts { self: Analyzer => cx = cx.outer // push further outward } - val defPre = pre - val defCx = cx - - while (foundInSuper && (cx ne NoContext) && (cx.scope ne null)) { - pre = cx.enclClass.prefix - val next = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope).orElse(searchPrefix) - if (next.exists && next.owner == cx.owner && thisContext.unit.exists && next.sourceFile == thisContext.unit.source.file) { - outerDefSym = next - cx = NoContext - } else - cx = cx.outer - } - if (outerDefSym.exists) { - if ((defSym.isAliasType || outerDefSym.isAliasType) && defPre.memberType(defSym) =:= pre.memberType(outerDefSym)) - outerDefSym = NoSymbol - if (defSym.isStable && outerDefSym.isStable && - (pre.memberType(outerDefSym).termSymbol == defSym || defPre.memberType(defSym).termSymbol == outerDefSym)) - outerDefSym = NoSymbol - } - - pre = defPre - cx = defCx - if (symbolDepth < 0) symbolDepth = cx.depth + def checkAmbiguousWithEnclosing(): Unit = if (foundInSuper && !thisContext.unit.isJava) { + val defPre = pre + val defCx = cx + + while ((cx ne NoContext) && (cx.owner == defCx.owner || cx.depth >= symbolDepth)) cx = cx.outer + + while ((cx ne NoContext) && (cx.scope ne null)) { + pre = cx.enclClass.prefix + val next = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope).orElse(searchPrefix) + if (next.exists && next.owner == cx.owner && thisContext.unit.exists && next.sourceFile == thisContext.unit.source.file) { + outerDefSym = next + cx = NoContext + } else + cx = cx.outer + } + if (outerDefSym.exists) { + if (outerDefSym == defSym) + outerDefSym = NoSymbol + else if ((defSym.isAliasType || outerDefSym.isAliasType) && defPre.memberType(defSym) =:= pre.memberType(outerDefSym)) + outerDefSym = NoSymbol + else if (defSym.isStable && outerDefSym.isStable && + (pre.memberType(outerDefSym).termSymbol == defSym || defPre.memberType(defSym).termSymbol == outerDefSym)) + outerDefSym = NoSymbol + } + + pre = defPre + cx = defCx + } + checkAmbiguousWithEnclosing() + var impSym: Symbol = NoSymbol val importCursor = new ImportCursor(thisContext, name) import importCursor.{imp1, imp2} diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala index f7407e7f8528..f8032312b84c 100644 --- a/test/files/pos/t11921b.scala +++ b/test/files/pos/t11921b.scala @@ -8,11 +8,11 @@ object test1 { object Test { val x = 1 class D extends C { - println(x) // error + println(x) } def f() = new C { - println(x) // error + println(x) } } } @@ -23,7 +23,7 @@ object test2 { val y = 2 } new D { - println(y) // error + println(y) } } } @@ -35,7 +35,7 @@ object test3 { } class E extends D { class F { - println(y) // error + println(y) } } } @@ -50,8 +50,8 @@ object test4 { val x = 1 class D extends C { def x(y: Int) = 3 - val y: Int = this.x // OK - val z: Int = x // OK + val y: Int = this.x + val z: Int = x } } } @@ -62,5 +62,25 @@ class C { val global = 42 } object D extends C { - println(global) // error + println(global) +} + +object test5 { + trait T { + val s: String + } + + trait U { + this: T => + val s: String + def t = s + } + + + class AA { + def f = 1 + class B extends AA { + def g = f + } + } } From 9f9e21feef31b7b5edc493294fd1731d2201e6eb Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 4 Feb 2023 18:06:42 -0800 Subject: [PATCH 167/261] Improve doc of Seq.sortWith --- src/library/scala/collection/Seq.scala | 33 +++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/library/scala/collection/Seq.scala b/src/library/scala/collection/Seq.scala index 36a54a48b94a..d960838fdcb7 100644 --- a/src/library/scala/collection/Seq.scala +++ b/src/library/scala/collection/Seq.scala @@ -735,22 +735,23 @@ trait SeqOps[+A, +CC[_], +C] extends Any } /** Sorts this $coll according to a comparison function. - * $willNotTerminateInf - * $willForceEvaluation - * - * The sort is stable. That is, elements that are equal (as determined by - * `lt`) appear in the same order in the sorted sequence as in the original. - * - * @param lt the comparison function which tests whether - * its first argument precedes its second argument in - * the desired ordering. - * @return a $coll consisting of the elements of this $coll - * sorted according to the comparison function `lt`. - * @example {{{ - * List("Steve", "Tom", "John", "Bob").sortWith(_.compareTo(_) < 0) = - * List("Bob", "John", "Steve", "Tom") - * }}} - */ + * $willNotTerminateInf + * $willForceEvaluation + * + * The sort is stable. That is, elements that are equal + * (`lt` returns false for both directions of comparison) + * appear in the same order in the sorted sequence as in the original. + * + * @param lt a predicate that is true if + * its first argument strictly precedes its second argument in + * the desired ordering. + * @return a $coll consisting of the elements of this $coll + * sorted according to the comparison function `lt`. + * @example {{{ + * List("Steve", "Bobby", "Tom", "John", "Bob").sortWith((x, y) => x.take(3).compareTo(y.take(3)) < 0) = + * List("Bobby", "Bob", "John", "Steve", "Tom") + * }}} + */ def sortWith(lt: (A, A) => Boolean): C = sorted(Ordering.fromLessThan(lt)) /** Sorts this $coll according to the Ordering which results from transforming From 2dc1960713fc471b1241ed5a156c6faf6a594e00 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 17 Feb 2023 01:47:10 -0800 Subject: [PATCH 168/261] String multiplification fix --- .../scala/collection/immutable/StringLike.scala | 14 ++++++++++---- .../collection/immutable/StringLikeTest.scala | 8 ++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/library/scala/collection/immutable/StringLike.scala b/src/library/scala/collection/immutable/StringLike.scala index 116295826eb8..cd5772284a27 100644 --- a/src/library/scala/collection/immutable/StringLike.scala +++ b/src/library/scala/collection/immutable/StringLike.scala @@ -18,6 +18,7 @@ import mutable.Builder import scala.util.matching.Regex import scala.math.ScalaNumber import scala.reflect.ClassTag +import java.lang.{StringBuilder => JStringBuilder} /** A companion object for the `StringLike` containing some constants. * @since 2.8 @@ -69,10 +70,15 @@ self => /** Return the current string concatenated `n` times. */ - def * (n: Int): String = { - val buf = new StringBuilder - for (i <- 0 until n) buf append toString - buf.toString + def *(n: Int): String = { + val s0 = toString + var ci = 0 max n + val sb = new JStringBuilder(s0.length * ci) + while (ci > 0) { + sb.append(s0) + ci -= 1 + } + sb.toString } override def compare(other: String) = toString compareTo other diff --git a/test/junit/scala/collection/immutable/StringLikeTest.scala b/test/junit/scala/collection/immutable/StringLikeTest.scala index a1d4c00e23cc..6e54ee7dd31b 100644 --- a/test/junit/scala/collection/immutable/StringLikeTest.scala +++ b/test/junit/scala/collection/immutable/StringLikeTest.scala @@ -75,4 +75,12 @@ class StringLikeTest { AssertUtil.assertThrows[java.lang.NullPointerException](sNull.toDouble) AssertUtil.assertThrows[java.lang.NullPointerException](sNull.toFloat) } + + @Test + def `the times they are achangin`(): Unit = { + assertEquals("xx", "x"*2) + assertEquals("x", "x"*1) + assertEquals("", "x"*0) + assertEquals("", "x" * -1) + } } From 02a33ea441d46444ec12e6ffa525e4e87c70fe7f Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sat, 18 Feb 2023 19:48:02 +0000 Subject: [PATCH 169/261] Update scala3-compiler_3, ... to 3.3.0-RC3 --- project/DottySupport.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index ae9a5dbabdfc..53862b8dc280 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.3.0-RC2" // TASTy version 28.3-1 + val supportedTASTyRelease = "3.3.0-RC3" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.2.0" + val dottyVersion = "3.3.0-RC3" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( From d689d96bcf507693de5d58d7d33ce6d39d2b0e71 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Sat, 18 Feb 2023 12:16:38 -0800 Subject: [PATCH 170/261] JLine 3.22.0 (was 3.21.0) --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index 1815121788da..f627f9c70eeb 100644 --- a/versions.properties +++ b/versions.properties @@ -9,5 +9,5 @@ starr.version=2.13.10 scala-asm.version=9.4.0-scala-1 # jna.version must be updated together with jline-terminal-jna -jline.version=3.21.0 +jline.version=3.22.0 jna.version=5.9.0 From 749b089c7d0a04405b7321473e81ca116cffd72e Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Sat, 18 Feb 2023 12:17:27 -0800 Subject: [PATCH 171/261] JNA 5.13.0 (was 5.9.0) --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index f627f9c70eeb..b491938bf301 100644 --- a/versions.properties +++ b/versions.properties @@ -10,4 +10,4 @@ scala-asm.version=9.4.0-scala-1 # jna.version must be updated together with jline-terminal-jna jline.version=3.22.0 -jna.version=5.9.0 +jna.version=5.13.0 From 61557d64f8ead0026ab3744d707adf13fa0465f3 Mon Sep 17 00:00:00 2001 From: AminMal Date: Sun, 19 Feb 2023 02:12:53 +0330 Subject: [PATCH 172/261] Implemented crossesTheEndAfterN, and used it in take and drop --- .../collection/immutable/NumericRange.scala | 73 +++++- .../immutable/NumericRangeTest.scala | 216 ++++++++++++++++++ .../immutable/NumericRangeProperties.scala | 56 +++++ 3 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala index 728fe3acbf54..d1ee494711a7 100644 --- a/src/library/scala/collection/immutable/NumericRange.scala +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -132,6 +132,75 @@ sealed class NumericRange[T]( // are forgiving: therefore the checks are with the methods. private def locationAfterN(n: Int): T = start + (step * fromInt(n)) + private def crossesTheEndAfterN(n: Int): Boolean = { + // if we're sure that subtraction in the context of T won't overflow, we use this function + // to calculate the length of the range + def unsafeRangeLength(r: NumericRange[T]): T = { + val diff = num.minus(r.end, r.start) + val quotient = num.quot(diff, r.step) + val remainder = num.rem(diff, r.step) + if (!r.isInclusive && num.equiv(remainder, num.zero)) + num.max(quotient, num.zero) + else + num.max(num.plus(quotient, num.one), num.zero) + } + + // detects whether value can survive a bidirectional trip to -and then from- Int. + def fitsInInteger(value: T): Boolean = num.equiv(num.fromInt(num.toInt(value)), value) + + val stepIsInTheSameDirectionAsStartToEndVector = + (num.gt(end, start) && num.gt(step, num.zero)) || (num.lt(end, start) && num.sign(step) == -num.one) + + if (num.equiv(start, end) || n <= 0 || !stepIsInTheSameDirectionAsStartToEndVector) return n >= 1 + + val sameSign = num.equiv(num.sign(start), num.sign(end)) + + if (sameSign) { // subtraction is safe + val len = unsafeRangeLength(this) + if (fitsInInteger(len)) n >= num.toInt(len) else num.gteq(num.fromInt(n), len) + } else { + // split to two ranges, which subtraction is safe in both of them (around zero) + val stepsRemainderToZero = num.rem(start, step) + val walksOnZero = num.equiv(stepsRemainderToZero, num.zero) + val closestToZero = if (walksOnZero) -step else stepsRemainderToZero + + /* + When splitting into two ranges, we should be super-careful about one of the sides hitting MinValue of T, + so we take two steps smaller than zero to ensure unsafeRangeLength won't overflow (taking one step may overflow depending on the step). + Same thing happens for MaxValue from zero, so we take one step further to ensure the safety of unsafeRangeLength. + After performing such operation, there are some elements remaining in between and around zero, + which their length is represented by carry. + */ + val (l: NumericRange[T], r: NumericRange[T], carry: Int) = + if (num.lt(start, num.zero)) { + if (walksOnZero) { + val twoStepsAfterLargestNegativeNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (NumericRange(start, closestToZero, step), copy(twoStepsAfterLargestNegativeNumber, end, step), 2) + } else { + (NumericRange(start, closestToZero, step), copy(num.plus(closestToZero, step), end, step), 1) + } + } else { + if (walksOnZero) { + val twoStepsAfterZero = num.times(step, num.fromInt(2)) + (copy(twoStepsAfterZero, end, step), NumericRange.inclusive(start, -step, step), 2) + } else { + val twoStepsAfterSmallestPositiveNumber = num.plus(closestToZero, num.times(step, num.fromInt(2))) + (copy(twoStepsAfterSmallestPositiveNumber, end, step), NumericRange.inclusive(start, closestToZero, step), 2) + } + } + + val leftLength = unsafeRangeLength(l) + val rightLength = unsafeRangeLength(r) + + // instead of `n >= rightLength + leftLength + curry` which may cause addition overflow, + // this can be used `(n - leftLength - curry) >= rightLength` (Both in Int and T, depends on whether the lengths fit in Int) + if (fitsInInteger(leftLength) && fitsInInteger(rightLength)) + n - num.toInt(leftLength) - carry >= num.toInt(rightLength) + else + num.gteq(num.minus(num.minus(num.fromInt(n), leftLength), num.fromInt(carry)), rightLength) + } + } + // When one drops everything. Can't ever have unchecked operations // like "end + 1" or "end - 1" because ranges involving Int.{ MinValue, MaxValue } // will overflow. This creates an exclusive range where start == end @@ -140,13 +209,13 @@ sealed class NumericRange[T]( override def take(n: Int): NumericRange[T] = { if (n <= 0 || isEmpty) newEmptyRange(start) - else if (n >= length) this + else if (crossesTheEndAfterN(n)) this else new NumericRange.Inclusive(start, locationAfterN(n - 1), step) } override def drop(n: Int): NumericRange[T] = { if (n <= 0 || isEmpty) this - else if (n >= length) newEmptyRange(end) + else if (crossesTheEndAfterN(n)) newEmptyRange(end) else copy(locationAfterN(n), end, step) } diff --git a/test/junit/scala/collection/immutable/NumericRangeTest.scala b/test/junit/scala/collection/immutable/NumericRangeTest.scala index c784ea8d9559..b007d918df6c 100644 --- a/test/junit/scala/collection/immutable/NumericRangeTest.scala +++ b/test/junit/scala/collection/immutable/NumericRangeTest.scala @@ -157,4 +157,220 @@ class NumericRangeTest { assertEquals(Nil, check(testDecreaseCase.start to testIncreaseCase.last by testIncreaseCase.step)) assertEquals(Nil, check(testDecreaseCase.inclusive)) } + + @Test + def numericRangeWithMoreThanMaxIntTake() = { + val start = BigInt(0) + val step = BigInt(1) + val end = BigInt(Int.MaxValue) * 5 + + // evaluation of length causes IllegalArgumentException because it cannot be represented as Int number + // Yet using `take` should be independent of evaluating length when it's not necessary. + assertThrows[IllegalArgumentException]((start until end by step).length) + val range = start until end by step + + val take = 100 + val smallChunkOfRange = range.take(take) + val expected = start until BigInt(100) by step + assertTrue(smallChunkOfRange equals expected) + assertTrue(smallChunkOfRange.length == take) + } + + @Test + def numericRangeWithMoreThanMaxIntDrop() = { + val start = BigInt(0) + val step = BigInt(1) + val toAddToMaxInt = 50 + val end = BigInt(Int.MaxValue) + toAddToMaxInt + + val drop = Int.MaxValue + + // evaluation of length causes IllegalArgumentException because it cannot be represented as Int number + // Yet using `drop` should be independent of evaluating length when it's not necessary. + assertThrows[IllegalArgumentException]((start until end by step).length) + val range = start until end by step + + val smallChunkOfRange = range.drop(drop) + val expected = BigInt(Int.MaxValue) until end by step + assertTrue(smallChunkOfRange equals expected) + assertTrue(smallChunkOfRange.length == toAddToMaxInt) + } + + @Test + def numericRangeSmallTypesDrop() = { + val byteStart: Byte = Byte.MinValue + val byteEnd: Byte = Byte.MaxValue + val drop = scala.util.Random.nextInt(Byte.MaxValue) + + val byteRange = NumericRange(byteStart, byteEnd, (1: Byte)) + val byteRangeChunk = byteRange.drop(drop) + assertTrue(byteRangeChunk.length == byteRange.length - drop) + assertTrue(byteRangeChunk.end == byteEnd) + assertTrue(byteRangeChunk.start == (byteStart + drop.toByte)) + + val shortStart: Short = Short.MinValue + val shortEnd: Short = Short.MaxValue + + val shortRange = NumericRange(shortStart, shortEnd, (1: Short)) + val shortRangeChunk = shortRange.drop(drop) + assertTrue(shortRangeChunk.length == shortRange.length - drop) + assertTrue(shortRangeChunk.end == shortEnd) + assertTrue(shortRangeChunk.start == (shortStart + drop.toShort)) + } + + @Test + def numericRangeSmallTypesTake() = { + val byteStart: Byte = Byte.MinValue + val byteEnd: Byte = Byte.MaxValue + val take = scala.util.Random.nextInt(Byte.MaxValue) + + val byteRange = NumericRange(byteStart, byteEnd, (1: Byte)) + val byteRangeChunk = byteRange.take(take) + assertTrue(byteRangeChunk.length == take) + assertTrue(byteRangeChunk.end == byteStart + take.toByte - 1) + assertTrue(byteRangeChunk.start == byteStart) + + val shortStart: Short = Short.MinValue + val shortEnd: Short = Short.MaxValue + + val shortRange = NumericRange(shortStart, shortEnd, (1: Short)) + val shortRangeChunk = shortRange.take(take) + assertTrue(shortRangeChunk.length == take) + assertTrue(shortRangeChunk.end == shortStart + take.toShort - 1) + assertTrue(shortRangeChunk.start == shortStart) + } + + @Test + def takeAndDropForCustomTypes() = { + import NumericRangeTest._ + + // smaller than Int + val start = NumericWrapper(Byte.MinValue) + val step = NumericWrapper(1: Byte) + val end = NumericWrapper(Byte.MaxValue) + val range = NumericRange.inclusive(start, end, step) + + val amount = scala.util.Random.nextInt(Byte.MaxValue * 2) + + val taken = range.take(amount) + val dropped = range.drop(amount) + + assertTrue(taken.start == range.start && taken.length == amount) + assertTrue(dropped.end == range.end && dropped.length == range.length - amount) + + // Int + val startInt = NumericWrapper(Int.MinValue) + val endInt = NumericWrapper(Int.MaxValue) + val stepInt = NumericWrapper(1) + + val intRange = NumericRange.inclusive(startInt, endInt, stepInt) + + val amountForInts = scala.util.Random.nextInt(Int.MaxValue) + + val takenInts = intRange.take(amountForInts) + val droppedInts = intRange.drop(amountForInts) + + assertTrue(takenInts.start == intRange.start && takenInts.length == amountForInts) + assertTrue(droppedInts.end == intRange.end && droppedInts.start.value == intRange.start.value + (amountForInts * intRange.step.value)) + + // Larger than int + + val startLong = NumericWrapper(Long.MinValue) + val stepLong = NumericWrapper(1L) + val endLong = NumericWrapper(Long.MaxValue) + + val longRange = NumericRange.inclusive(startLong, endLong, stepLong) + + val amountForLongs = scala.util.Random.nextInt(Int.MaxValue) + + val takenLongs = longRange.take(amountForLongs) + val droppedLongs = longRange.drop(amountForLongs) + + assertTrue(takenLongs.start == longRange.start && takenLongs.length == amountForLongs) + assertTrue(droppedLongs.end == longRange.end && droppedLongs.start.value == longRange.start.value + (amountForLongs * longRange.step.value)) + } + + @Test + def wideIntNumericRangeTakeAndDrop() = { + val start = Int.MinValue + val end = Int.MaxValue + val step = 1 + + val amount = util.Random.nextInt(Int.MaxValue) + + val range = NumericRange(start, end, step) + + assertThrows[IllegalArgumentException](range.length) + assertTrue(range.take(amount).length == amount) + val dropped = range.drop(amount) + assertTrue(dropped.end == range.end && dropped.step == range.step && dropped.start == (amount * step) + range.start) + } + + @Test + def wideNegativeNumericRangeUntilZeroShouldNotOverflow() = { + val start = Int.MinValue + val end = 0 + val step = 1 + + val amount = util.Random.nextInt(Int.MaxValue) + + val range = NumericRange.inclusive(start, end, step) + + assertThrows[IllegalArgumentException](range.length) + + val taken = range.take(amount) + assertTrue(taken.length == amount) + assertTrue(taken.start == range.start && taken.step == range.step) + + + val dropped = range.drop(amount) + assertTrue(dropped.end == range.end && dropped.step == range.step && dropped.start == (amount * step) + range.start) + } + +} + +object NumericRangeTest { + + private case class NumericWrapper[T](value: T) + private object NumericWrapper { + implicit def isIntegral[T](implicit tNum: Integral[T]): Integral[NumericWrapper[T]] = new Integral[NumericWrapper[T]] { + override def quot(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.quot(x.value, y.value)) + + override def rem(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.rem(x.value, y.value)) + + override def plus(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.plus(x.value, y.value)) + + override def minus(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.minus(x.value, y.value)) + + override def times(x: NumericWrapper[T], y: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.times(x.value, y.value)) + + override def negate(x: NumericWrapper[T]): NumericWrapper[T] = + NumericWrapper(tNum.negate(x.value)) + + override def fromInt(x: Int): NumericWrapper[T] = + NumericWrapper(tNum.fromInt(x)) + + override def parseString(str: String): Option[NumericWrapper[T]] = + tNum.parseString(str).map(NumericWrapper.apply) + + override def toInt(x: NumericWrapper[T]): Int = + tNum.toInt(x.value) + + override def toLong(x: NumericWrapper[T]): Long = + tNum.toLong(x.value) + + override def toFloat(x: NumericWrapper[T]): Float = + tNum.toFloat(x.value) + + override def toDouble(x: NumericWrapper[T]): Double = + tNum.toDouble(x.value) + + override def compare(x: NumericWrapper[T], y: NumericWrapper[T]): Int = tNum.compare(x.value, y.value) + } + } } \ No newline at end of file diff --git a/test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala b/test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala new file mode 100644 index 000000000000..ce758ef7546f --- /dev/null +++ b/test/scalacheck/scala/collection/immutable/NumericRangeProperties.scala @@ -0,0 +1,56 @@ +package scala +package collection.immutable + +import org.scalacheck._ + +import scala.util.Try + +class NumericRangeProperties extends Properties("immutable.NumericRange") { + import Prop._ + import NumericRangeProperties._ + + property("same length when take and drop with a specific amount (Byte)") = forAll { (r: NumericRange[Byte], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } + + property("same length when take and drop with a specific amount (Short)") = forAll { (r: NumericRange[Short], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } + + property("same length when take and drop with a specific amount (Int)") = forAll { (r: NumericRange[Int], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } + + // This is commented intentionally, because of a bug in NumericRange.count, + // which makes the length unstable for property based testing: + // e.g., NumericRange(1L, -9223372036854775808L, -1L).length == 1 +// property("same length when take and drop with a specific amount (Long)") = forAll { (r: NumericRange[Long], amount: Int) => +// Try(r.length).isSuccess ==> { +// r.take(amount).length + r.drop(amount).length == r.length +// } +// } + + property("same length when take and drop with a specific amount (BigInt)") = forAll { (r: NumericRange[BigInt], amount: Int) => + Try(r.length).isSuccess ==> { + r.take(amount).length + r.drop(amount).length == r.length + } + } +} + +object NumericRangeProperties { + private implicit def arbitraryNumericRange[T](implicit num: Integral[T], tGen: Arbitrary[T]): Arbitrary[NumericRange[T]] = + Arbitrary( + for { + start <- tGen.arbitrary + end <- tGen.arbitrary + step <- tGen.arbitrary filterNot (num.equiv(_, num.zero)) + incl <- Gen.oneOf(true, false) + } yield if (incl) NumericRange.inclusive(start, end, step) else NumericRange(start, end, step) + ) +} \ No newline at end of file From 4960a7a6ea30da4f01e972c005227e9777ea561f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 19 Feb 2023 00:24:51 -0800 Subject: [PATCH 173/261] Re-revert untokenized argfile, write quoted args --- src/compiler/scala/tools/nsc/CompilerCommand.scala | 5 +++-- src/compiler/scala/tools/nsc/Global.scala | 4 +++- test/files/run/argfile.scala | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index dc606d5a15a0..9e2405ee6e66 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -126,11 +126,12 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { def expandArg(arg: String): List[String] = { import java.nio.file.{Files, Paths} import scala.jdk.CollectionConverters._ - def stripComment(s: String) = s.takeWhile(_ != '#').trim() // arg can be "" but not " " + def stripComment(s: String) = s.takeWhile(_ != '#').trim() val file = Paths.get(arg.stripPrefix("@")) if (!Files.exists(file)) throw new java.io.FileNotFoundException(s"argument file $file could not be found") - Files.readAllLines(file).asScala.filter(!_.startsWith("#")).map(stripComment).toList + val lines = Files.readAllLines(file).asScala.map(stripComment).filterNot(_.isEmpty).toList + lines.flatMap(settings.splitParams) } // override this if you don't want arguments processed here diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c3b369df35e4..89da5fe0ac59 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1492,7 +1492,9 @@ class Global(var currentSettings: Settings, reporter0: Reporter) private def printArgs(sources: List[SourceFile]): Unit = settings.printArgs.valueSetByUser foreach { value => - val argsFile = (settings.recreateArgs ::: sources.map(_.file.absolute.toString())).mkString("", "\n", "\n") + def quote(s: String) = if (s.charAt(0) != '"' && s.contains(' ')) "\"" + s + "\"" else s + val allArgs = settings.recreateArgs ::: sources.map(_.file.absolute.toString()) + val argsFile = allArgs.map(quote).mkString("", "\n", "\n") value match { case "-" => reporter.echo(argsFile) diff --git a/test/files/run/argfile.scala b/test/files/run/argfile.scala index 6fffbac7e9bc..5fdb17fc988e 100644 --- a/test/files/run/argfile.scala +++ b/test/files/run/argfile.scala @@ -23,7 +23,8 @@ object Test extends DirectTest { } def code = sm""" - |@annotation.nowarn + |import annotation.* + |@nowarn |final class C { | def f: Int = "42".toInt |} From 65739724793296594429e2ad0ca2bc9381092bd7 Mon Sep 17 00:00:00 2001 From: Peng Cheng Date: Sat, 19 Feb 2022 17:54:48 -0500 Subject: [PATCH 174/261] Backport bugfixes from splain 0.5.8 & 1.0.0, errors from -VImplicits now show complete implicit search tree --- .../nsc/typechecker/splain/SplainData.scala | 25 +- .../typechecker/splain/SplainFormatting.scala | 390 +++++++++++++++--- test/files/run/splain-tree.check | 53 ++- test/files/run/splain-tree.scala | 7 +- test/files/run/splain.check | 6 +- 5 files changed, 403 insertions(+), 78 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala index 39a405796c95..d00442686f99 100644 --- a/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala @@ -14,6 +14,7 @@ package scala.tools.nsc package typechecker package splain +import scala.annotation.tailrec import scala.util.matching.Regex trait SplainData { @@ -68,10 +69,20 @@ trait SplainData { } object ImplicitError { - def unapplyCandidate(e: ImplicitError): Tree = - e.candidate match { - case TypeApply(fun, _) => fun - case a => a + def unapplyCandidate(e: ImplicitError): Tree = unapplyRecursively(e.candidate) + + @tailrec + private def unapplyRecursively(tree: Tree): Tree = + tree match { + case TypeApply(fun, _) => unapplyRecursively(fun) + case Apply(fun, _) => unapplyRecursively(fun) + case a => a + } + + def cleanCandidate(e: ImplicitError): String = + unapplyCandidate(e).toString match { + case ImplicitError.candidateRegex(suf) => suf + case a => a } def candidateName(e: ImplicitError): String = @@ -83,12 +94,6 @@ trait SplainData { val candidateRegex: Regex = """.*\.this\.(.*)""".r - def cleanCandidate(e: ImplicitError): String = - unapplyCandidate(e).toString match { - case candidateRegex(suf) => suf - case a => a - } - def shortName(ident: String): String = ident.substring(ident.lastIndexOf(".") + 1) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala index b283485b6086..88e5d868751c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala @@ -14,8 +14,9 @@ package scala.tools.nsc package typechecker package splain +import scala.collection.immutable.{List, Nil, Seq} import scala.collection.mutable -import scala.reflect.internal.TypeDebugging.AnsiColor._ +import scala.language.implicitConversions class FormatCache[K, V](cache: mutable.Map[K, V]) { def apply(k: K, orElse: => V): V = cache.getOrElseUpdate(k, orElse) @@ -28,7 +29,10 @@ object FormatCache { trait SplainFormatters { self: Analyzer => - import global._, definitions._ + import global._ + import definitions._ + + implicit def asSimpleName(s: String): SimpleName = SimpleName(s) def formatType(tpe: Type, top: Boolean): Formatted @@ -74,33 +78,71 @@ trait SplainFormatters { } object RefinedFormatter extends SpecialFormatter { + object DeclSymbol { def unapply(sym: Symbol): Option[(Formatted, Formatted)] = - if (sym.hasRawInfo) Some((Simple(SimpleName(sym.simpleName.toString)), formatType(sym.rawInfo, true))) + if (sym.hasRawInfo) Some((Simple(sym.simpleName.toString), formatType(sym.rawInfo, top = true))) else None } - def ignoredTypes: List[Type] = List(typeOf[Object], typeOf[Any], typeOf[AnyRef]) + lazy val ignoredTypes: List[Type] = List(typeOf[Object], typeOf[Any], typeOf[AnyRef]) + + def sanitizeParents: List[Type] => List[Type] = { ps => + val tpes = ps.distinct + val result = tpes.filterNot(t => ignoredTypes.exists(_ =:= t)) - def sanitizeParents: List[Type] => List[Type] = { - case List(tpe) => List(tpe) - case tpes => tpes.filter(t => !ignoredTypes.exists(_ =:= t)) + if (result.isEmpty) tpes.headOption.toList + else result + } + + object Refined { + def unapply(tpe: Type): Option[(List[Type], Scope)] = + tpe match { + case TypeRef(pre, sym, List(RefinedType(parents, decls))) + if decls.isEmpty && pre.typeSymbol.fullName == "zio" && sym.fullName == "zio.Has" => + val sanitized = sanitizeParents(parents) + if (sanitized.length == 1) + Some((List(TypeRef(pre, sym, sanitized.headOption.toList)), decls)) + else + None + case RefinedType(types, scope) => + if (scope.isEmpty) { + val subtypes = types.map(_.dealias).flatMap { + case Refined(types, _) => + types + case tpe => + List(tpe) + } + Some((subtypes, scope)) + } else + Some((types, scope)) + case t@SingleType(_, _) => + unapply(t.underlying) + case _ => + None + } } def formatDecl: Symbol => Formatted = { case DeclSymbol(n, t) => Decl(n, t) - case sym => Simple(SimpleName(sym.toString)) + case sym => Simple(sym.toString) } - def apply[A]( - tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, - )(rec: (A, Boolean) => Formatted): Option[Formatted] = tpe match { - case Refined(parents, decls) => - Some(RefinedForm(sanitizeParents(parents).map(formatType(_, top)), decls.toList.map(formatDecl))) - case _ => None + override def apply[A]( + tpe: Type, + simple: String, + args: List[A], + formattedArgs: => List[Formatted], + top: Boolean + )(rec: (A, Boolean) => Formatted): Option[Formatted] = { + tpe match { + case Refined(parents, decls) => + Some(RefinedForm(sanitizeParents(parents).map(formatType(_, top)), decls.toList.map(formatDecl))) + case _ => None + } } - val none: Formatted = Simple(SimpleName("")) + val none: Formatted = Simple("") def separate[A](left: List[A], right: List[A]): (List[A], List[A], List[A]) = { val leftS = Set(left: _*) @@ -112,13 +154,22 @@ trait SplainFormatters { } def matchTypes(left: List[Type], right: List[Type]): List[Formatted] = { - val (common, uniqueLeft, uniqueRight) = separate(left.map(formatType(_, true)), right.map(formatType(_, true))) - val diffs = uniqueLeft.zipAll(uniqueRight, none, none).map { case (l, r) => Diff(l, r) } - common ::: diffs + val (common, uniqueLeft, uniqueRight) = + separate(left.map(formatType(_, top = true)), right.map(formatType(_, top = true))) + val diffs = uniqueLeft + .zipAll(uniqueRight, none, none) + .map { + case (l, r) => + Diff(l, r) + } + common ++ diffs } def filterDecls(syms: List[Symbol]): List[(Formatted, Formatted)] = - syms.collect { case DeclSymbol(sym, rhs) => (sym, rhs) } + syms.collect { + case DeclSymbol(sym, rhs) => + (sym, rhs) + } def matchDecls(left: List[Symbol], right: List[Symbol]): List[Formatted] = { val (common, uniqueLeft, uniqueRight) = separate(filterDecls(left), filterDecls(right)) @@ -130,7 +181,9 @@ trait SplainFormatters { case (None, Some((sym, r))) => DeclDiff(sym, none, r) case (Some((sym, l)), None) => DeclDiff(sym, l, none) } - common.map { case (sym, rhs) => Decl(sym, rhs) } ++ diffs + common.map { + case (sym, rhs) => Decl(sym, rhs) + } ++ diffs } def diff(left: Type, right: Type, top: Boolean): Option[Formatted] = @@ -155,11 +208,247 @@ trait SplainFormatters { } } +object SplainFormatting { + + import scala.reflect.internal.TypeDebugging.AnsiColor._ + + val ELLIPSIS: String = "⋮".blue +} + trait SplainFormatting extends SplainFormatters { self: Analyzer => import global._ + import SplainFormatting._ + import scala.reflect.internal.TypeDebugging.AnsiColor._ + + case class ImplicitErrorLink( + fromTree: ImplicitError, + fromHistory: DivergentImplicitTypeError + ) { + + val sameCandidateTree: Boolean = fromTree.candidate equalsStructure fromHistory.underlyingTree + + val samePendingType: Boolean = fromTree.specifics match { + case ss: ImplicitErrorSpecifics.NotFound => + fromHistory.pt0 =:= ss.param.tpe + case _ => + false + } + + val moreSpecificPendingType: Boolean = fromTree.specifics match { + case ss: ImplicitErrorSpecifics.NotFound => + fromHistory.pt0 <:< ss.param.tpe + case _ => + false + } + + val sameStartingWith: Boolean = { + fromHistory.sym.fullLocationString == fromTree.candidate.symbol.fullLocationString + } + + lazy val divergingSearchStartingWithHere: Boolean = sameStartingWith + + lazy val divergingSearchDiscoveredHere: Boolean = sameCandidateTree && moreSpecificPendingType + } + + object ImplicitErrorLink {} + + case class ImplicitErrorTree( + error: ImplicitError, + children: Seq[ImplicitErrorTree] = Nil + ) { + + import ImplicitErrorTree._ + + def doCollectFull(alwaysDisplayRoot: Boolean = false): Seq[ErrorNode] = + if (children.isEmpty) Seq(ErrorNode(error, alwaysShow = true)) + else { + + Seq(ErrorNode(error, alwaysShow = alwaysDisplayRoot)) ++ { + + if (children.size >= 2) children.flatMap(_.doCollectFull(true)) + else children.flatMap(_.doCollectFull(false)) + } + } + + lazy val collectFull: Seq[ErrorNode] = doCollectFull(true) + + lazy val collectCompact: Seq[ErrorNode] = { + + val displayed = collectFull.zipWithIndex.filter { + case (v, _) => + v.alwaysShow + } + + val ellipsisIndices = displayed.map(_._2 - 1).toSet + (collectFull.size - 1) + + val withEllipsis = displayed.map { + case (v, i) => + if (!ellipsisIndices.contains(i)) v.copy(showEllipsis = true) + else v + } + + withEllipsis + } + + case class FormattedChain( + source: Seq[ErrorNode] + ) { + + val toList: List[String] = { + val collected = source.toList + val baseIndent = collected.headOption.map(_.nesting).getOrElse(0) + + val formatted = collected.map { v => + val formatted = v.formatted + if (v.showEllipsis) formatted.copy(_2 = formatted._2 :+ ELLIPSIS) + else formatted + } + + indentTree(formatted, baseIndent) + } + + override lazy val toString: String = toList.mkString("\n") + } + + object FormattedChain { + + object Full extends FormattedChain(collectFull) + + object Compact extends FormattedChain(collectCompact) + + val display: FormattedChain = if (settings.VimplicitsVerboseTree.value) Full else Compact + } + + override def toString: String = FormattedChain.Full.toString + } + + object ImplicitErrorTree { + + case class ErrorNode( + error: ImplicitError, + alwaysShow: Boolean, + showEllipsis: Boolean = false + ) { + + def nesting: RunId = error.nesting + + val formatted: (String, List[String], RunId) = + formatNestedImplicit(error) + } + + def fromNode( + Node: ImplicitError, + offsprings: List[ImplicitError] + ): ImplicitErrorTree = { + val topNesting = Node.nesting + + val children = fromChildren( + offsprings, + topNesting + ) + + ImplicitErrorTree(Node, children) + } + + def fromChildren( + offsprings: List[ImplicitError], + topNesting: Int + ): List[ImplicitErrorTree] = { + + if (offsprings.isEmpty) + return Nil + + val minNesting = offsprings.map(v => v.nesting).min + + if (minNesting < topNesting + 1) + throw new InternalError( + "Detail: nesting level of offsprings of an implicit search tree node should be higher" + ) + + val wII = offsprings.zipWithIndex + + val childrenII = wII + .filter { + case (sub, _) => + if (sub.nesting < minNesting) { + throw new InternalError( + s"Detail: Sub-node in implicit tree can only have nesting level larger than top node," + + s" but (${sub.nesting} < $minNesting)" + ) + } + + sub.nesting == minNesting + } + .map(_._2) + + val ranges = { + + val seqs = (childrenII ++ Seq(offsprings.size)) + .sliding(2) + .toList + + seqs.map { + case Seq(from, until) => + from -> until + case _ => + throw new InternalError("Detail: index should not be empty") + } + } + + val children = ranges.map { range => + val _top = offsprings(range._1) + + val _offsprings = offsprings.slice(range._1 + 1, range._2) + + fromNode( + _top, + _offsprings + ) + } + + mergeDuplicates(children) + // children + } + + def mergeDuplicates(children: List[ImplicitErrorTree]): List[ImplicitErrorTree] = { + val errors = children.map(_.error).distinct + + val grouped = errors.map { ee => + val group = children.filter(c => c.error == ee) + + val mostSpecificError = group.head.error + // TODO: this old design is based on a huge hypothesis, should it be improved + // val mostSpecificError = group.map(_.error).maxBy(v => v.candidate.toString.length) + + val allChildren = group.flatMap(v => v.children) + val mergedChildren = mergeDuplicates(allChildren) + + ImplicitErrorTree(mostSpecificError, mergedChildren) + } + + grouped.distinctBy(v => v.FormattedChain.Full.toString) // TODO: this may lose information + } + } + + + def formatNestedImplicit(err: ImplicitError): (String, List[String], Int) = { + + val candidate = ImplicitError.cleanCandidate(err) + val problem = s"${candidate.red} invalid because" + val reason = err.specifics match { + case e: ImplicitErrorSpecifics.NotFound => implicitMessage(e.param, NoImplicitFoundAnnotation(err.candidate, e.param)._2) + case e: ImplicitErrorSpecifics.NonconformantBounds => formatNonConfBounds(e) + } + val base = (problem, reason, err.nesting) + + val reasons = base._2 + + (problem, reasons, base._3) + } + val breakInfixLength: Int = 70 def dealias(tpe: Type) = @@ -451,16 +740,6 @@ trait SplainFormatting extends SplainFormatters { List("nonconformant bounds;", types.red, params.green) } - def formatNestedImplicit(err: ImplicitError): (String, List[String], Int) = { - val candidate = ImplicitError.cleanCandidate(err) - val problem = s"${candidate.red} invalid because" - val reason = err.specifics match { - case e: ImplicitErrorSpecifics.NotFound => implicitMessage(e.param, NoImplicitFoundAnnotation(err.candidate, e.param)._2) - case e: ImplicitErrorSpecifics.NonconformantBounds => formatNonConfBounds(e) - } - (problem, reason, err.nesting) - } - def hideImpError(error: ImplicitError): Boolean = error.specifics match { case ImplicitErrorSpecifics.NonconformantBounds(_, _, _) => true case ImplicitErrorSpecifics.NotFound(_) => false @@ -480,41 +759,12 @@ trait SplainFormatting extends SplainFormatters { def deepestLevel(chain: List[ImplicitError]) = chain.foldLeft(0)((z, a) => if (a.nesting > z) a.nesting else z) - def formatImplicitChainTreeCompact(chain: List[ImplicitError]): Option[List[String]] = { - chain.headOption.map { head => - val max = deepestLevel(chain) - val leaves = chain.drop(1).dropWhile(_.nesting < max) - val base = if (head.nesting == 0) 0 else 1 - val (fhh, fht, fhn) = formatNestedImplicit(head) - val spacer = if (leaves.nonEmpty && leaves.length < chain.length) List("⋮".blue) else Nil - val fh = (fhh, fht ++ spacer, fhn) - val ft = leaves.map(formatNestedImplicit) - indentTree(fh :: ft, base) - } - } - def formatImplicitChainTreeFull(chain: List[ImplicitError]): List[String] = formatIndentTree(chain, chain.headOption.map(_.nesting).getOrElse(0)) def formatImplicitChainFlat(chain: List[ImplicitError]): List[String] = chain.map(formatNestedImplicit).flatMap { case (h, t, _) => h :: t } - def formatImplicitChain(chain: List[ImplicitError]): List[String] = { - val compact = if (settings.VimplicitsVerboseTree.value) None else formatImplicitChainTreeCompact(chain) - compact.getOrElse(formatImplicitChainTreeFull(chain)) - } - - /** Remove duplicates and special cases that should not be shown. - * In some cases, candidates are reported twice, once as `Foo.f` and once as - * `f`. `ImplicitError.equals` checks the simple names for identity, which - * is suboptimal, but works for 99% of cases. - * Special cases are handled in [[hideImpError]] */ - def formatNestedImplicits(errors: List[ImplicitError]) = { - val visible = errors.filterNot(hideImpError) - val chains = splitChains(visible).map(_.distinct).distinct - chains.map(formatImplicitChain).flatMap("" :: _).drop(1) - } - def implicitMessage(param: Symbol, annotationMsg: String): List[String] = { val tpe = param.tpe val msg = if (annotationMsg.isEmpty) Nil else annotationMsg.split("\n").toList.map(_.blue) :+ "" @@ -535,6 +785,24 @@ trait SplainFormatting extends SplainFormatters { } } - def formatImplicitError(param: Symbol, errors: List[ImplicitError], annotationMsg: String) = - ("implicit error;" :: implicitMessage(param, annotationMsg) ::: formatNestedImplicits(errors)).mkString("\n") + def formatImplicitError( + param: Symbol, + errors: List[ImplicitError], + annotationMsg: String + ): String = { + + val msg = implicitMessage(param, annotationMsg) + val errorTrees = ImplicitErrorTree.fromChildren(errors, -1) + + val errorTreesStr = errorTrees.map(_.FormattedChain.display.toString) + + val components: Seq[String] = + Seq("implicit error;") ++ + msg ++ + errorTreesStr + + val result = components.mkString("\n") + + result + } } diff --git a/test/files/run/splain-tree.check b/test/files/run/splain-tree.check index 2e3c5b2597db..ce4973924d5d 100644 --- a/test/files/run/splain-tree.check +++ b/test/files/run/splain-tree.check @@ -16,12 +16,10 @@ i1a invalid because !I p: tpes.I8 ――――――――――――――i8 invalid because !I p: tpes.I9 - ――――――――――i6b invalid because !I p: tpes.I8 ――――――――――――i8 invalid because !I p: tpes.I9 - ――――i3b invalid because !I p: tpes.I4 ――――――i4 invalid because @@ -34,7 +32,10 @@ i1a invalid because !I p: tpes.I8 ――――――――――――――i8 invalid because !I p: tpes.I9 - +――――――――――i6b invalid because + !I p: tpes.I8 +――――――――――――i8 invalid because + !I p: tpes.I9 i1b invalid because !I p: tpes.I6 ――i6a invalid because @@ -43,5 +44,51 @@ i1b invalid because !I p: tpes.I8 ――――――i8 invalid because !I p: tpes.I9 +――i6b invalid because + !I p: tpes.I8 +――――i8 invalid because + !I p: tpes.I9 implicitly[I1] ^ +newSource1.scala:28: error: implicit error; +!I e: tpes.I1 +i1a invalid because +!I p: tpes.I2 +⋮ +――i3a invalid because + !I p: tpes.I4 + ⋮ +――――i6a invalid because + !I p: tpes.I7 + ⋮ +――――――――i8 invalid because + !I p: tpes.I9 +――――i6b invalid because + !I p: tpes.I8 +――――――i8 invalid because + !I p: tpes.I9 +――i3b invalid because + !I p: tpes.I4 + ⋮ +――――i6a invalid because + !I p: tpes.I7 + ⋮ +――――――――i8 invalid because + !I p: tpes.I9 +――――i6b invalid because + !I p: tpes.I8 +――――――i8 invalid because + !I p: tpes.I9 +i1b invalid because +!I p: tpes.I6 +――i6a invalid because + !I p: tpes.I7 + ⋮ +――――――i8 invalid because + !I p: tpes.I9 +――i6b invalid because + !I p: tpes.I8 +――――i8 invalid because + !I p: tpes.I9 + implicitly[I1] + ^ \ No newline at end of file diff --git a/test/files/run/splain-tree.scala b/test/files/run/splain-tree.scala index d660ee85d3f2..56f9ff7a3f16 100644 --- a/test/files/run/splain-tree.scala +++ b/test/files/run/splain-tree.scala @@ -1,7 +1,7 @@ import scala.tools.partest._ object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Vimplicits -Vimplicits-verbose-tree" + override def extraSettings: String = "-usejavacp -Vimplicits" def code: String = "" @@ -39,9 +39,12 @@ object Tree def show(): Unit = { val global = newCompiler() + val globalVerbose = newCompiler("-Vimplicits-verbose-tree") - def run(code: String): Unit = + def run(code: String): Unit = { + compileString(globalVerbose)(code.trim) compileString(global)(code.trim) + } run(verboseTree) } diff --git a/test/files/run/splain.check b/test/files/run/splain.check index 9dbb8db96b7c..9c41024605b2 100644 --- a/test/files/run/splain.check +++ b/test/files/run/splain.check @@ -2,7 +2,6 @@ newSource1.scala:13: error: implicit error; !I e: ImplicitChain.II ImplicitChain.g invalid because !I impPar3: ImplicitChain.I1 -⋮ ――ImplicitChain.i1 invalid because !I impPar7: ImplicitChain.I3 implicitly[II] @@ -13,6 +12,10 @@ newSource1.scala:6: error: type mismatch; ^ newSource1.scala:7: error: implicit error; !I e: Bounds.F[Bounds.Arg] +Bounds.g invalid because +nonconformant bounds; +[Bounds.Arg, scala.Nothing] +[A <: Bounds.Base, B] implicitly[F[Arg]] ^ newSource1.scala:4: error: implicit error; @@ -116,7 +119,6 @@ Ordering.ordered invalid because !I asComparable: java.lang.Object => java.lang.Comparable[_$2] No implicit view available from Object => Comparable[_ >: Object]. -⋮ Ordering.comparatorToOrdering invalid because !I cmp: java.util.Comparator[java.lang.Object] ms.map(_ => o) From 92e0c362fd5b433e3938c3600b4035f8143cbb1b Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 14 Feb 2023 14:57:57 +0100 Subject: [PATCH 175/261] Lint for integral divisions that are widened to a float --- src/compiler/scala/tools/nsc/Reporting.scala | 1 + .../scala/tools/nsc/settings/Warnings.scala | 2 ++ .../scala/tools/nsc/typechecker/Typers.scala | 21 ++++++++++++++++--- src/library/scala/math/BigInt.scala | 1 + src/library/scala/runtime/RichInt.scala | 1 + .../scala/runtime/ScalaNumberProxy.scala | 1 + .../scala/reflect/internal/Definitions.scala | 7 +++++++ .../scala/reflect/internal/TreeInfo.scala | 4 ++-- .../reflect/runtime/JavaUniverseForce.scala | 1 + test/files/neg/lint-int-div-to-float.check | 18 ++++++++++++++++ test/files/neg/lint-int-div-to-float.scala | 18 ++++++++++++++++ test/files/run/is-valid-num.scala | 2 +- 12 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 test/files/neg/lint-int-div-to-float.check create mode 100644 test/files/neg/lint-int-div-to-float.scala diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index 1257ffc5ddb0..b396d9ab8676 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -381,6 +381,7 @@ object Reporting { object LintEtaZero extends Lint; add(LintEtaZero) object LintEtaSam extends Lint; add(LintEtaSam) object LintDeprecation extends Lint; add(LintDeprecation) + object LintIntDivToFloat extends Lint; add(LintIntDivToFloat) object LintBynameImplicit extends Lint; add(LintBynameImplicit) object LintRecurseWithDefault extends Lint; add(LintRecurseWithDefault) object LintUnitSpecialization extends Lint; add(LintUnitSpecialization) diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 39bcb235ca58..3f0ae6357e66 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -183,6 +183,7 @@ trait Warnings { val Constant = LintWarning("constant", "Evaluation of a constant arithmetic expression results in an error.") val Unused = LintWarning("unused", "Enable -Ywarn-unused:imports,privates,locals,implicits,nowarn.") val Deprecation = LintWarning("deprecation", "Enable -deprecation and also check @deprecated annotations.") + val IntDivToFloat = LintWarning("int-div-to-float", "Warn when an integer division is converted (widened) to floating point: `(someInt / 2): Double`.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -207,6 +208,7 @@ trait Warnings { def warnConstant = lint contains Constant def lintUnused = lint contains Unused def lintDeprecation = lint contains Deprecation + def lintIntDivToFloat = lint contains IntDivToFloat // Lint warnings that are currently -Y, but deprecated in that usage @deprecated("Use warnAdaptedArgs", since="2.11.2") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3e9210da1343..35326af385de 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -874,7 +874,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // refuses to re-attempt typechecking, and presumes that someone // else was responsible for issuing the related type error! fun.setSymbol(NoSymbol) - case _ => } debuglog(s"fallback on implicits: ${tree}/$resetTree") // scala/bug#10066 Need to patch the enclosing tree in the context to make translation of Dynamic @@ -1094,9 +1093,25 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } if (!isThisTypeResult) context.warning(tree.pos, "discarded non-Unit value", WarningCategory.WFlagValueDiscard) } - @inline def warnNumericWiden(): Unit = + @inline def warnNumericWiden(target: Symbol): Unit = { // not `context.deprecationWarning` because they are not buffered in silent mode if (!isPastTyper && settings.warnNumericWiden) context.warning(tree.pos, "implicit numeric widening", WarningCategory.WFlagNumericWiden) + object warnIntDiv extends Traverser { + def isInt(t: Tree) = ScalaIntegralValueClasses(t.tpe.typeSymbol) + override def traverse(tree: Tree): Unit = tree match { + case Apply(Select(q, nme.DIV), _) if isInt(q) => + context.warning(tree.pos, s"integral division is implicitly converted (widened) to floating point. Add an explicit `.to${target.name}`.", WarningCategory.LintIntDivToFloat) + case Apply(Select(a1, _), List(a2)) if isInt(tree) && isInt(a1) && isInt(a2) => + traverse(a1) + traverse(a2) + case Select(q, _) if isInt(tree) && isInt(q) => + traverse(q) + case _ => + } + } + if (!isPastTyper && settings.lintIntDivToFloat && (target == FloatClass || target == DoubleClass)) + warnIntDiv(tree) + } // The <: Any requirement inhibits attempts to adapt continuation types to non-continuation types. val anyTyped = tree.tpe <:< AnyTpe @@ -1105,7 +1120,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case TypeRef(_, UnitClass, _) if anyTyped => // (12) warnValueDiscard() ; tpdPos(gen.mkUnitBlock(tree)) case TypeRef(_, numValueCls, _) if anyTyped && isNumericValueClass(numValueCls) && isNumericSubType(tree.tpe, pt) => // (10) (11) - warnNumericWiden() ; tpdPos(Select(tree, s"to${numValueCls.name}")) + warnNumericWiden(numValueCls) ; tpdPos(Select(tree, s"to${numValueCls.name}")) case dealiased if dealiased.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt) => // (13) tpd(adaptAnnotations(tree, this, mode, pt)) case _ => diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index 627b23f31821..99312b820a9c 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -172,6 +172,7 @@ final class BigInt(val bigInteger: BigInteger) (shifted.signum != 0) && !(shifted equals BigInt.minusOne) } + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole() = true def underlying = bigInteger diff --git a/src/library/scala/runtime/RichInt.scala b/src/library/scala/runtime/RichInt.scala index 4d1ae66976a4..b5deccaf4e84 100644 --- a/src/library/scala/runtime/RichInt.scala +++ b/src/library/scala/runtime/RichInt.scala @@ -31,6 +31,7 @@ final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] wit /** Returns `'''true'''` if this number has no decimal component. * Always `'''true'''` for `RichInt`. */ + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole() = true override def isValidInt = true diff --git a/src/library/scala/runtime/ScalaNumberProxy.scala b/src/library/scala/runtime/ScalaNumberProxy.scala index 4f809efca975..f4a55d32e1bb 100644 --- a/src/library/scala/runtime/ScalaNumberProxy.scala +++ b/src/library/scala/runtime/ScalaNumberProxy.scala @@ -45,6 +45,7 @@ trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Typed def signum = num.signum(self) } trait ScalaWholeNumberProxy[T] extends Any with ScalaNumberProxy[T] { + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole() = true } trait IntegralProxy[T] extends Any with ScalaWholeNumberProxy[T] with RangedProxy[T] { diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7b271e782e28..ae9d497bbb83 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -180,6 +180,13 @@ trait Definitions extends api.StandardDefinitions { } def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses + lazy val ScalaIntegralValueClasses: Set[Symbol] = Set( + CharClass, + ByteClass, + ShortClass, + IntClass, + LongClass) + def underlyingOfValueClass(clazz: Symbol): Type = clazz.derivedValueClassUnbox.tpe.resultType diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index bf5692053ca2..a93146af057c 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -845,10 +845,10 @@ abstract class TreeInfo { object Applied { def apply(tree: Tree): Applied = new Applied(tree) - def unapply(applied: Applied): Option[(Tree, List[Tree], List[List[Tree]])] = + def unapply(applied: Applied): Some[(Tree, List[Tree], List[List[Tree]])] = Some((applied.core, applied.targs, applied.argss)) - def unapply(tree: Tree): Option[(Tree, List[Tree], List[List[Tree]])] = + def unapply(tree: Tree): Some[(Tree, List[Tree], List[List[Tree]])] = unapply(dissectApplied(tree)) } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 7819c389ecda..75795814a9dd 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -498,6 +498,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ScalaValueClasses definitions.ScalaValueClassesSet definitions.ScalaNumericValueClassesSet + definitions.ScalaIntegralValueClasses uncurry.VarargsSymbolAttachment uncurry.DesugaredParameterType diff --git a/test/files/neg/lint-int-div-to-float.check b/test/files/neg/lint-int-div-to-float.check new file mode 100644 index 000000000000..2c7f86bc44ff --- /dev/null +++ b/test/files/neg/lint-int-div-to-float.check @@ -0,0 +1,18 @@ +lint-int-div-to-float.scala:6: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w1: Double = f / 2 + ^ +lint-int-div-to-float.scala:7: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w2: Double = (f / 2) * 3 + ^ +lint-int-div-to-float.scala:8: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w3: Double = -(f / 2) + ^ +lint-int-div-to-float.scala:9: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w4: Double = (new C).f / (new C).f * 3 + ^ +lint-int-div-to-float.scala:10: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w5: Double = f - f.abs / 2 + ^ +error: No warnings can be incurred under -Xfatal-warnings. +5 warnings found +one error found diff --git a/test/files/neg/lint-int-div-to-float.scala b/test/files/neg/lint-int-div-to-float.scala new file mode 100644 index 000000000000..4f66c481384e --- /dev/null +++ b/test/files/neg/lint-int-div-to-float.scala @@ -0,0 +1,18 @@ +// scalac: -Xlint -Xfatal-warnings + +class C { + def f = 1 + + def w1: Double = f / 2 + def w2: Double = (f / 2) * 3 + def w3: Double = -(f / 2) + def w4: Double = (new C).f / (new C).f * 3 + def w5: Double = f - f.abs / 2 + + def o1: Double = (f / 2).toDouble + def o2: Double = f.toDouble / 2 + def o3: Double = f / 2.toDouble + def o4: Double = f / 2d + def o5: Double = (new C).f.toDouble / (new C).f * 3 + def o6: Long = f / 2 + 3 // only warn if widening to a floating point, not when widening int to long +} diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index 156121cab59f..c003c091e6a3 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warnings; re-run with + * filter: re-run with -deprecation */ object Test { def x = BigInt("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") From 194bb8f06a405619c4a747805fa0ae163023315b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E5=A2=83=E8=BF=B7=E7=A6=BB?= Date: Tue, 5 Oct 2021 00:55:44 +0800 Subject: [PATCH 176/261] Consistent access modifiers for specilized overload Create specialized overload from overridgin (not overridden) symbol to ensure consistent access modifiers. --- .../scala/tools/nsc/transform/SpecializeTypes.scala | 2 +- test/files/pos/t10211.scala | 12 ++++++++++++ test/files/run/t6028.check | 4 ++-- test/files/run/t6555.check | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 test/files/pos/t10211.scala diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 8d4d9f8417c4..d92583161629 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1125,7 +1125,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } else None case (overridden, env) => - val om = specializedOverload(clazz, overridden, env) + val om = specializedOverload(clazz, overriding, env, overridden) clazz.info.decls.enter(om) foreachWithIndex(om.paramss) { (params, i) => foreachWithIndex(params) { (param, j) => diff --git a/test/files/pos/t10211.scala b/test/files/pos/t10211.scala new file mode 100644 index 000000000000..709e7cf41c42 --- /dev/null +++ b/test/files/pos/t10211.scala @@ -0,0 +1,12 @@ +trait QuitePrivate[@specialized(Int) K] { + protected[this] def hasK(k :K) :Boolean +} + +trait MoreOpen extends QuitePrivate[Int] { + override def hasK(k :Int) :Boolean +} + + +object Playground extends App { + def check(guy :MoreOpen) = guy.hasK(42) +} \ No newline at end of file diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index 771a7cbff201..2707d3507765 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -30,7 +30,7 @@ package { () }; final def apply(): Int = $anonfun$foo$1.this.apply$mcI$sp(); - def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); + final def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); private[this] val $outer: T = _; def $outer(): T = $anonfun$foo$1.this.$outer; def apply(): Object = scala.Int.box($anonfun$foo$1.this.apply()); @@ -68,7 +68,7 @@ package { () }; final def apply(): Unit = $anonfun$tryy$1.this.apply$mcV$sp(); - def apply$mcV$sp(): Unit = try { + final def apply$mcV$sp(): Unit = try { $anonfun$tryy$1.this.tryyLocal$1.elem = $anonfun$tryy$1.this.tryyParam$1 } finally (); private[this] val $outer: T = _; diff --git a/test/files/run/t6555.check b/test/files/run/t6555.check index 71ed070c83d7..d20c1fe9ca67 100644 --- a/test/files/run/t6555.check +++ b/test/files/run/t6555.check @@ -12,7 +12,7 @@ package { () }; final def apply(param: Int): Int = $anonfun.this.apply$mcII$sp(param); - def apply$mcII$sp(param: Int): Int = param + final def apply$mcII$sp(param: Int): Int = param }; (new <$anon: Int => Int>(): Int => Int) }; From 47abf034ba2c4692ab64e5ac2aa9caa1bd9d0c1d Mon Sep 17 00:00:00 2001 From: AminMal Date: Wed, 22 Feb 2023 03:50:01 +0330 Subject: [PATCH 177/261] Removed random values in NumericRange unit tests --- .../collection/immutable/NumericRangeTest.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/junit/scala/collection/immutable/NumericRangeTest.scala b/test/junit/scala/collection/immutable/NumericRangeTest.scala index b007d918df6c..be36632a95da 100644 --- a/test/junit/scala/collection/immutable/NumericRangeTest.scala +++ b/test/junit/scala/collection/immutable/NumericRangeTest.scala @@ -200,7 +200,7 @@ class NumericRangeTest { def numericRangeSmallTypesDrop() = { val byteStart: Byte = Byte.MinValue val byteEnd: Byte = Byte.MaxValue - val drop = scala.util.Random.nextInt(Byte.MaxValue) + val drop = 10 val byteRange = NumericRange(byteStart, byteEnd, (1: Byte)) val byteRangeChunk = byteRange.drop(drop) @@ -222,7 +222,7 @@ class NumericRangeTest { def numericRangeSmallTypesTake() = { val byteStart: Byte = Byte.MinValue val byteEnd: Byte = Byte.MaxValue - val take = scala.util.Random.nextInt(Byte.MaxValue) + val take = 10 val byteRange = NumericRange(byteStart, byteEnd, (1: Byte)) val byteRangeChunk = byteRange.take(take) @@ -250,7 +250,7 @@ class NumericRangeTest { val end = NumericWrapper(Byte.MaxValue) val range = NumericRange.inclusive(start, end, step) - val amount = scala.util.Random.nextInt(Byte.MaxValue * 2) + val amount = 20 val taken = range.take(amount) val dropped = range.drop(amount) @@ -265,7 +265,7 @@ class NumericRangeTest { val intRange = NumericRange.inclusive(startInt, endInt, stepInt) - val amountForInts = scala.util.Random.nextInt(Int.MaxValue) + val amountForInts = 40 val takenInts = intRange.take(amountForInts) val droppedInts = intRange.drop(amountForInts) @@ -281,7 +281,7 @@ class NumericRangeTest { val longRange = NumericRange.inclusive(startLong, endLong, stepLong) - val amountForLongs = scala.util.Random.nextInt(Int.MaxValue) + val amountForLongs = 50 val takenLongs = longRange.take(amountForLongs) val droppedLongs = longRange.drop(amountForLongs) @@ -296,7 +296,7 @@ class NumericRangeTest { val end = Int.MaxValue val step = 1 - val amount = util.Random.nextInt(Int.MaxValue) + val amount = 50 val range = NumericRange(start, end, step) @@ -312,7 +312,7 @@ class NumericRangeTest { val end = 0 val step = 1 - val amount = util.Random.nextInt(Int.MaxValue) + val amount = 50 val range = NumericRange.inclusive(start, end, step) From ad96fc4550d7bef262114801766d4f12a6b8b298 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 21 Feb 2023 23:14:06 -0800 Subject: [PATCH 178/261] Reification knows only first param list is case accessor --- .../reflect/internal/ReificationSupport.scala | 10 +++++++--- .../pos/t10589-case-implicit-param/cc_2.scala | 6 ++++++ .../t10589-case-implicit-param/macros_1.scala | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 test/files/pos/t10589-case-implicit-param/cc_2.scala create mode 100644 test/files/pos/t10589-case-implicit-param/macros_1.scala diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index 4a02e4eba0c3..dbc4a82b4e03 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -124,7 +124,7 @@ trait ReificationSupport { self: SymbolTable => def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation) def mkParam(argss: List[List[Tree]], extraFlags: FlagSet = NoFlags, excludeFlags: FlagSet = DEFERRED): List[List[ValDef]] = - argss.map { args => args.map { mkParam(_, extraFlags, excludeFlags) } } + argss.map(_.map(mkParam(_, extraFlags, excludeFlags))) def mkParam(tree: Tree, extraFlags: FlagSet, excludeFlags: FlagSet): ValDef = tree match { case Typed(Ident(name: TermName), tpt) => @@ -344,8 +344,12 @@ trait ReificationSupport { self: SymbolTable => def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], constrMods: Modifiers, vparamss: List[List[Tree]], earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = { - val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L) - val vparamss0 = mkParam(vparamss, extraFlags, excludeFlags = DEFERRED | PARAM) + val extraCaseFlags = if (mods.isCase) CASEACCESSOR else 0L + val excludeFlags = DEFERRED | PARAM + val vparamss0 = + if (vparamss.isEmpty) vparamss.asInstanceOf[List[List[ValDef]]] + else mkParam(vparamss.head :: Nil, PARAMACCESSOR | extraCaseFlags, excludeFlags) ++ + mkParam(vparamss.tail, PARAMACCESSOR, excludeFlags) val tparams0 = mkTparams(tparams) val parents0 = gen.mkParents(mods, if (mods.isCase) parents.filter { diff --git a/test/files/pos/t10589-case-implicit-param/cc_2.scala b/test/files/pos/t10589-case-implicit-param/cc_2.scala new file mode 100644 index 000000000000..17a1dce15ca9 --- /dev/null +++ b/test/files/pos/t10589-case-implicit-param/cc_2.scala @@ -0,0 +1,6 @@ +// scalac: -Ymacro-annotations +trait T[A] + +@macid +case class CC[A: T](x: A) + diff --git a/test/files/pos/t10589-case-implicit-param/macros_1.scala b/test/files/pos/t10589-case-implicit-param/macros_1.scala new file mode 100644 index 000000000000..700c925d879c --- /dev/null +++ b/test/files/pos/t10589-case-implicit-param/macros_1.scala @@ -0,0 +1,19 @@ +// scalac: -Ymacro-annotations + +import scala.annotation.StaticAnnotation +import scala.language.experimental.macros +import scala.reflect.macros.whitebox.Context + +class macid extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro macidMacro.impl +} +object macidMacro { + def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { + new Macros[c.type](c).macidMacroImpl(annottees.toList) + } +} +class Macros[C <: Context](val c: C) { + import c.universe._ + def macidMacroImpl(annottees: List[c.Expr[Any]]): c.Expr[Any] = + annottees(0) +} From b5d04650d4350129c2e37083ddac5b3994df4a06 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 21 Feb 2023 16:34:08 +0100 Subject: [PATCH 179/261] [nomerge] Deal with overloads in new ambiguity warning --- .../tools/nsc/typechecker/Contexts.scala | 22 +++---- test/files/neg/t11921b.check | 11 +++- test/files/neg/t11921b.scala | 58 ++++++++++++++++++- test/files/pos/t11921b.scala | 45 ++------------ 4 files changed, 83 insertions(+), 53 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 2a9df72b351f..b111e9f5fdbf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -56,22 +56,24 @@ trait Contexts { self: Analyzer => LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - def ambiguousWithEnclosing(sym: Symbol, other: Symbol, otherEnclClass: Symbol) = + def ambiguousWithEnclosing(sym: Symbol, other: Symbol, symEnclClass: Symbol) = if (!currentRun.isScala213) None else { - val enclDesc = if (otherEnclClass.isAnonymousClass) "anonymous class" else otherEnclClass.toString - val parent = otherEnclClass.parentSymbols.find(_.isNonBottomSubClass(other.owner)).getOrElse(NoSymbol) - val inherit = if (parent.exists && parent != other.owner) s", inherited through parent $parent" else "" + val sym1 = sym.alternatives.head + val other1 = other.alternatives.head + val enclDesc = if (symEnclClass.isAnonymousClass) "anonymous class" else symEnclClass.toString + val parent = symEnclClass.parentSymbols.find(_.isNonBottomSubClass(other1.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != other1.owner) s", inherited through parent $parent" else "" val message = - s"""it is both defined in the enclosing ${sym.owner} and available in the enclosing $enclDesc as $other (defined in ${other.ownsString}$inherit) + s"""it is both defined in the enclosing ${sym1.owner} and available in the enclosing $enclDesc as $other1 (defined in ${other1.ownsString}$inherit) |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |To continue using the symbol from the superclass, write `this.${sym.name}`.""".stripMargin + |To continue using the symbol from the superclass, write `this.${sym1.name}`.""".stripMargin if (currentRun.isScala3) Some(LookupAmbiguous(message)) else { // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning other.updateAttachment( LookupAmbiguityWarning( - s"""reference to ${sym.name} is ambiguous; + s"""reference to ${sym1.name} is ambiguous; |$message |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) None @@ -1257,7 +1259,7 @@ trait Contexts { self: Analyzer => defSym = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope) match { case NoSymbol => val prefixSym = searchPrefix - if (currentRun.isScala213 && prefixSym.exists && prefixSym.owner != cx.owner) + if (currentRun.isScala213 && prefixSym.exists && prefixSym.alternatives.forall(_.owner != cx.owner)) foundInSuper = true prefixSym case found => @@ -1278,8 +1280,8 @@ trait Contexts { self: Analyzer => while ((cx ne NoContext) && (cx.scope ne null)) { pre = cx.enclClass.prefix - val next = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope).orElse(searchPrefix) - if (next.exists && next.owner == cx.owner && thisContext.unit.exists && next.sourceFile == thisContext.unit.source.file) { + val next = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope).orElse(searchPrefix).filter(_.owner == cx.owner) + if (next.exists && thisContext.unit.exists && next.sourceFile == thisContext.unit.source.file) { outerDefSym = next cx = NoContext } else diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 3d47ac28c834..cb377ba343f2 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -38,8 +38,15 @@ it is both defined in the enclosing object Uhu and available in the enclosing cl Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. - def t = x // ambiguous, message mentions parent B + def t = x // error, message mentions parent B ^ +t11921b.scala:132: warning: reference to a is ambiguous; +it is both defined in the enclosing class C and available in the enclosing trait J as method a (defined in trait I) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.a`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + val t = a // error + ^ error: No warnings can be incurred under -Xfatal-warnings. -6 warnings found +7 warnings found one error found diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index c6218948901d..1d6f1ad24edb 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -72,8 +72,64 @@ object test5 { val x = 2 class C extends B { class Inner { - def t = x // ambiguous, message mentions parent B + def t = x // error, message mentions parent B } } } } + +object test6 { + trait T { + val s: String + } + + trait U { + this: T => + val s: String + def t = s // ok + } + + + class AA { + def f = 1 + class B extends AA { + def g = f // ok, same symbol + } + } +} + +object test7 { + trait T { + // overloaded a + val a = "" + def a(x: Int) = "" + } + + trait I { + val a = 1 + } + + class C extends T { + trait J { + self: I => + // no warning here. when checking for an outer `a`, we find an OverloadedSymbol with the two definitions in `T`. + // The owner of the overloaded symbol is `C`, but the alternatives have owner `T`. + val t = a + } + } +} + + +object test8 { + trait I { + // overloaded a + val a = 1 + def a(x: Int) = "" + } + class C { + val a = "" + trait J extends I { + val t = a // error + } + } +} diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala index f8032312b84c..354f50624e6f 100644 --- a/test/files/pos/t11921b.scala +++ b/test/files/pos/t11921b.scala @@ -8,11 +8,11 @@ object test1 { object Test { val x = 1 class D extends C { - println(x) + println(x) // error } def f() = new C { - println(x) + println(x) // error } } } @@ -23,7 +23,7 @@ object test2 { val y = 2 } new D { - println(y) + println(y) // error } } } @@ -35,52 +35,17 @@ object test3 { } class E extends D { class F { - println(y) + println(y) // error } } } } -object test4 { - - class C { - val x = 0 - } - object Test { - val x = 1 - class D extends C { - def x(y: Int) = 3 - val y: Int = this.x - val z: Int = x - } - } -} - object global class C { val global = 42 } object D extends C { - println(global) -} - -object test5 { - trait T { - val s: String - } - - trait U { - this: T => - val s: String - def t = s - } - - - class AA { - def f = 1 - class B extends AA { - def g = f - } - } + println(global) // error } From 0418c67f019c7ab1239cefc46a5ddfb9a8ce8b2e Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 22 Feb 2023 10:16:13 +0100 Subject: [PATCH 180/261] shuffle tests the pos test tests wconf to ignore the ambiguity, so we can't use it to test absence of the warning --- test/files/neg/t11921b.check | 9 ++++++++- test/files/neg/t11921b.scala | 37 +++++++++++++++++++++++++++++++++++- test/files/pos/t11921b.scala | 18 +----------------- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 9c1ad36598e7..4132d97d9426 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -33,6 +33,13 @@ To continue using the symbol from the superclass, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def t = x // ambiguous, message mentions parent B ^ +t11921b.scala:111: warning: reference to a is ambiguous; +it is both defined in the enclosing class C and available in the enclosing trait J as value a (defined in trait I) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.a`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + val t = a + ^ error: No warnings can be incurred under -Werror. -5 warnings +6 warnings 1 error diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index 433d17a8c5c2..ab2464b2ee9b 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -62,7 +62,7 @@ class C { val global = 42 } object D extends C { - println(global) // OK, since global is defined in package + println(global) // OK, since global is defined in package (https://github.com/scala/scala/pull/10220/files#r1109773904) } object test5 { @@ -77,3 +77,38 @@ object test5 { } } } + +object test6 { + trait I { + val a = 1 + def a(x: Int) = "" + } + class C { + val a = "" + trait J extends I { + val t = a // error + } + } +} + + +object test7 { + trait T { + // overloaded a + val a = "" + def a(x: Int) = "" + } + + trait I { + val a = 1 + } + + class C extends T { + trait J { + self: I => + // no warning here. when checking for an outer `a`, we find an OverloadedSymbol with the two definitions in `T`. + // The owner of the overloaded symbol is `C`, but the alternatives have owner `T`. + val t = a + } + } +} diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala index ba9a370cb89a..b6c0ea293d04 100644 --- a/test/files/pos/t11921b.scala +++ b/test/files/pos/t11921b.scala @@ -1,4 +1,3 @@ - // scalac: -Werror -Wconf:msg=legacy-binding:s object test1 { @@ -42,26 +41,11 @@ object test3 { } } -object test4 { - - class C { - val x = 0 - } - object Test { - val x = 1 - class D extends C { - def x(y: Int) = 3 - val y: Int = this.x // OK - val z: Int = x // OK - } - } -} - object global class C { val global = 42 } object D extends C { - println(global) // OK, since global is defined in package + println(global) // OK, since global is defined in package (https://github.com/scala/scala/pull/10220/files#r1109773904) } From 3486bcd4500cbb64b97e78a177156a32a123df55 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 22 Feb 2023 10:16:34 +0100 Subject: [PATCH 181/261] Deal with overloads in new ambiguity warning --- .../scala/tools/nsc/typechecker/Contexts.scala | 14 ++++++++------ test/files/neg/t11921b.check | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 47d8b5a25045..6a37dbc6d9ac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1364,19 +1364,21 @@ trait Contexts { self: Analyzer => LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") def ambiguousDefinitions(sym: Symbol, other: Symbol, otherFoundInSuper: Boolean, otherEnclClass: Symbol) = { if (otherFoundInSuper) { + val sym1 = sym.alternatives.head + val other1 = other.alternatives.head val enclDesc = if (otherEnclClass.isAnonymousClass) "anonymous class" else otherEnclClass.toString - val parent = otherEnclClass.parentSymbols.find(_.isNonBottomSubClass(other.owner)).getOrElse(NoSymbol) - val inherit = if (parent.exists && parent != other.owner) s", inherited through parent $parent" else "" + val parent = otherEnclClass.parentSymbols.find(_.isNonBottomSubClass(other1.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != other1.owner) s", inherited through parent $parent" else "" val message = - s"""it is both defined in the enclosing ${sym.owner} and available in the enclosing $enclDesc as $other (defined in ${other.ownsString}$inherit) + s"""it is both defined in the enclosing ${sym1.owner} and available in the enclosing $enclDesc as $other1 (defined in ${other1.ownsString}$inherit) |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |To continue using the symbol from the superclass, write `this.${sym.name}`.""".stripMargin + |To continue using the symbol from the superclass, write `this.${sym1.name}`.""".stripMargin if (currentRun.isScala3) Some(LookupAmbiguous(message)) else { // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning other.updateAttachment(LookupAmbiguityWarning( - s"""reference to ${sym.name} is ambiguous; + s"""reference to ${sym1.name} is ambiguous; |$message |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) None @@ -1492,7 +1494,7 @@ trait Contexts { self: Analyzer => (lastPre.memberType(lastDef).termSymbol == defSym || pre.memberType(defSym).termSymbol == lastDef)) defSym = NoSymbol foundInPrefix = inPrefix && defSym.exists - foundInSuper = foundInPrefix && defSym.owner != cx.owner + foundInSuper = foundInPrefix && defSym.alternatives.forall(_.owner != cx.owner) } nextDefinition(NoSymbol, NoPrefix) diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 4132d97d9426..733629877624 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -33,12 +33,12 @@ To continue using the symbol from the superclass, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def t = x // ambiguous, message mentions parent B ^ -t11921b.scala:111: warning: reference to a is ambiguous; -it is both defined in the enclosing class C and available in the enclosing trait J as value a (defined in trait I) +t11921b.scala:89: warning: reference to a is ambiguous; +it is both defined in the enclosing class C and available in the enclosing trait J as method a (defined in trait I) Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. To continue using the symbol from the superclass, write `this.a`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. - val t = a + val t = a // error ^ error: No warnings can be incurred under -Werror. 6 warnings From 9c63254a8dcb01fa528d0dabeb9d57f311510866 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 23 Feb 2023 12:06:43 +0100 Subject: [PATCH 182/261] Fix spurious ambiguity warning Forgot to save and restore `symbolDepth` when checking for potentially ambiguous outer definitions. --- .../tools/nsc/typechecker/Contexts.scala | 2 ++ test/files/pos/t11921-depth.scala | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 test/files/pos/t11921-depth.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index b111e9f5fdbf..ec1520aa66df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1275,6 +1275,7 @@ trait Contexts { self: Analyzer => def checkAmbiguousWithEnclosing(): Unit = if (foundInSuper && !thisContext.unit.isJava) { val defPre = pre val defCx = cx + val defDepth = symbolDepth while ((cx ne NoContext) && (cx.owner == defCx.owner || cx.depth >= symbolDepth)) cx = cx.outer @@ -1299,6 +1300,7 @@ trait Contexts { self: Analyzer => pre = defPre cx = defCx + symbolDepth = defDepth } checkAmbiguousWithEnclosing() diff --git a/test/files/pos/t11921-depth.scala b/test/files/pos/t11921-depth.scala new file mode 100644 index 000000000000..6b02294c87cd --- /dev/null +++ b/test/files/pos/t11921-depth.scala @@ -0,0 +1,21 @@ +// scalac: -Xfatal-warnings -Xsource:2.13 + +package p.q.test { + class K +} + +package test { + class NimicDeloc +} + +package m { + import p.q._ + + abstract class T { + def test = 1 + } + + class C extends T { + def m = test + } +} From 78dc4779dc9ab3e1602bb40324522fb8adf015a0 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 23 Feb 2023 12:11:08 +0100 Subject: [PATCH 183/261] Save and restore symbol depth when when searching outer defs --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6a37dbc6d9ac..e3217f0cdee1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1601,6 +1601,7 @@ trait Contexts { self: Analyzer => val defSym0 = defSym val pre0 = pre val cx0 = cx + val depth0 = symbolDepth val wasFoundInSuper = foundInSuper val foundCompetingSymbol: () => Boolean = if (foreignDefined) () => !foreignDefined @@ -1623,6 +1624,7 @@ trait Contexts { self: Analyzer => defSym = defSym0 pre = pre0 cx = cx0 + symbolDepth = depth0 } if (preferDef) impSym = NoSymbol else defSym = NoSymbol From ba25d9045b2cc138afa4a4d13768da34ea921500 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Sun, 26 Feb 2023 16:28:04 -0800 Subject: [PATCH 184/261] fix `test/files/jvm/natives.scala` on ARM Macs fixes scala/scala-dev#833 --- test/files/jvm/.gitignore | 1 + test/files/jvm/libnatives-arm.jnilib | Bin 0 -> 16818 bytes ...ibnatives.jnilib => libnatives-x86.jnilib} | Bin test/files/jvm/mkLibNatives.sh | 42 +++++++++++++++--- test/files/jvm/natives.scala | 18 +++++--- 5 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 test/files/jvm/.gitignore create mode 100755 test/files/jvm/libnatives-arm.jnilib rename test/files/jvm/{libnatives.jnilib => libnatives-x86.jnilib} (100%) diff --git a/test/files/jvm/.gitignore b/test/files/jvm/.gitignore new file mode 100644 index 000000000000..21888692e907 --- /dev/null +++ b/test/files/jvm/.gitignore @@ -0,0 +1 @@ +natives.o diff --git a/test/files/jvm/libnatives-arm.jnilib b/test/files/jvm/libnatives-arm.jnilib new file mode 100755 index 0000000000000000000000000000000000000000..114622219efd40ba0dffbf74450e96c1095d9d65 GIT binary patch literal 16818 zcmeI3O-NKx6vxlZXaA9VVrSphsmnSsFCO(B?Mj2$MR7Gk#18N->La z(YPyc-^LOoA%#F9ToomOeL!g~Qj17h6l0{F^WGbo#1dTpFZ}Ph=bU@byZ6W2p682S zm+V9q;<1U9&AM$RiczkFsDL%cQp#KD^GamZ(AM_TIV|E*`cNto=!}>So7MFO4jZ{0 z<`(gjXZ27T^_nT&DAg9~2!`raFx1e(k=6Acn)Ry8L*oBcFSQu2<(;xusZ*Ys>dIrD zX3ZuH&b)#tsT5UzVmpYW4dI*?dVqry_Sma zTiqOUvW&X?I!l(mwsDLs<+eR+j6X#JMEoz&0hZK~yA$o=-@rm;ZQ<53ezNFnG#m*u zmmVptj~Wno*G`ty@o7Yx*%q6=yjw;gOY(7zsA!{)ulq5+5=t@iBuEGZ|WfWBubcH6<<3$zJ(X zD?QiJJI{UPx<<`Dc=4rY+s?h%xn?&vH+JhS+D?v_m|~O6%F^uN)|bX)Q78TFe&r2> zBg*A+xyx19AFT>BHMLNojRtzmt;vOaAcrrT?C*Edzt79j#rO7czCjT$lU?3<9OE*U zY_g`wg`{MN-atN?4@^5 zZ9l$$xFI%WU%unM*E2ZmYnhmD7*BgOIoW8f^`$R&Kj^+1d;6%b>PE@k_3||@-n5QA zsjcwTeR*1t|1H1n_35#^jBQ7w?;J}PM!G)LoY-=0-qqFj^H=qN^IYc-k9Rcl^H}lF M-1(62J{@ZO18wuk761SM literal 0 HcmV?d00001 diff --git a/test/files/jvm/libnatives.jnilib b/test/files/jvm/libnatives-x86.jnilib similarity index 100% rename from test/files/jvm/libnatives.jnilib rename to test/files/jvm/libnatives-x86.jnilib diff --git a/test/files/jvm/mkLibNatives.sh b/test/files/jvm/mkLibNatives.sh index 537187eeddbb..38c782985890 100755 --- a/test/files/jvm/mkLibNatives.sh +++ b/test/files/jvm/mkLibNatives.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh -e ############################################################################## # Author : Stephane Micheloud @@ -11,15 +11,25 @@ debug= cygwin=false; -darwin=false; +darwin_x86=false; +darwin_arm=false; case "`uname`" in - CYGWIN*) cygwin=true ;; - Darwin*) darwin=true ;; + CYGWIN*) cygwin=true ;; + Darwin*) case "`uname -m`" in + x86_64*) darwin_x86=true ;; + arm64*) darwin_arm=true ;; + esac esac CLASS_NAME=Test\$ CLASS_DIR=natives-jvm.obj +if [ ! -f "${CLASS_DIR}/${CLASS_NAME}.class" ]; then + echo "first you need to run this within sbt:" + echo "partest --debug test/files/jvm/natives.scala" + exit +fi + OBJ_NAME=natives LIB_NAME=libnatives @@ -34,13 +44,31 @@ fi JAVAH=${JAVA_HOME}/bin/javah JAVAH_OPTIONS="-jni -force -classpath ${CLASS_DIR} -o ${OBJ_NAME}.h" +if [ ! -f "${JAVAH}" ]; then + # Oracle removed `javah`. The replacement is `javac -h`, but + # requiring 8 seems fine for now, especially since we commit + # the generated files to version control, so this script hardly + # ever needs to be run at all + echo "this script only works on Java 8" + exit +fi + CC=gcc -if $darwin; then - CC_OPTIONS="-c -arch ppc -arch i386 -arch x86_64" +if $darwin_x86; then + # not sure if this stuff still works on current MacOS -- the + # generated .jnilib file is already in version control and we're not + # likely to need to generate it again, so I didn't bother to see if this + # needs the same changes that are in the darwin_arm section below + CC_OPTIONS="-c -arch i386 -arch x86_64" CC_INCLUDES="-I/System/Library/Frameworks/JavaVM.framework/Headers" LNK_OPTIONS="-dynamiclib -framework JavaVM" - FULL_LIB_NAME=${LIB_NAME}.jnilib + FULL_LIB_NAME=${LIB_NAME}-x86.jnilib +elif $darwin_arm; then + CC_OPTIONS="-c -arch arm64" + CC_INCLUDES="-I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin" + LNK_OPTIONS="-L${JAVA_HOME}/jre/lib/server -dynamiclib -ljvm" + FULL_LIB_NAME=${LIB_NAME}-arm.jnilib else CC_OPTIONS=-c CC_INCLUDES="-I${JAVA_HOME}/include -I${JAVA_HOME}/include/${OSTYPE}" diff --git a/test/files/jvm/natives.scala b/test/files/jvm/natives.scala index 14ee4e1c1b03..6620b5d8cd28 100644 --- a/test/files/jvm/natives.scala +++ b/test/files/jvm/natives.scala @@ -2,14 +2,18 @@ object Test { //println("java.library.path=" + System.getProperty("java.library.path")) - val sysWordSize = System.getProperty("sun.arch.data.model", "32") - val sysType = System.getProperty("os.name") + val os = System.getProperty("os.name") + val arch = System.getProperty("os.arch") - val libName = - if (sysType == "Mac OS X") - "natives" - else - "natives-" + sysWordSize + val libName = (os, arch) match { + case ("Mac OS X", "aarch64") => + "natives-arm" + case ("Mac OS X", "x86_64") => + "natives-x86" + case _ => + val wordSize = System.getProperty("sun.arch.data.model", "32") + "natives-" + wordSize + } System.loadLibrary(libName) From a0e4495087c708c97278c0610d55f9a726178956 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 27 Feb 2023 16:21:48 +0100 Subject: [PATCH 185/261] don't warn if the found symbol is implicit --- .../tools/nsc/typechecker/Contexts.scala | 26 +++++++++---------- test/files/neg/t11921b.check | 13 ++++++++-- test/files/neg/t11921b.scala | 24 +++++++++++++++++ 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index ec1520aa66df..6321fb051b2b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -56,24 +56,24 @@ trait Contexts { self: Analyzer => LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - def ambiguousWithEnclosing(sym: Symbol, other: Symbol, symEnclClass: Symbol) = - if (!currentRun.isScala213) None else { - val sym1 = sym.alternatives.head - val other1 = other.alternatives.head - val enclDesc = if (symEnclClass.isAnonymousClass) "anonymous class" else symEnclClass.toString - val parent = symEnclClass.parentSymbols.find(_.isNonBottomSubClass(other1.owner)).getOrElse(NoSymbol) - val inherit = if (parent.exists && parent != other1.owner) s", inherited through parent $parent" else "" + def ambiguousWithEnclosing(outer: Symbol, inherited: Symbol, currentClass: Symbol) = + if (!currentRun.isScala213 || !outer.exists || inherited.isImplicit) None else { + val outer1 = outer.alternatives.head + val inherited1 = inherited.alternatives.head + val classDesc = if (currentClass.isAnonymousClass) "anonymous class" else currentClass.toString + val parent = currentClass.parentSymbols.find(_.isNonBottomSubClass(inherited1.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != inherited1.owner) s", inherited through parent $parent" else "" val message = - s"""it is both defined in the enclosing ${sym1.owner} and available in the enclosing $enclDesc as $other1 (defined in ${other1.ownsString}$inherit) + s"""it is both defined in the enclosing ${outer1.owner} and available in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |To continue using the symbol from the superclass, write `this.${sym1.name}`.""".stripMargin + |To continue using the symbol from the superclass, write `this.${outer1.name}`.""".stripMargin if (currentRun.isScala3) Some(LookupAmbiguous(message)) else { // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning - other.updateAttachment( + inherited.updateAttachment( LookupAmbiguityWarning( - s"""reference to ${sym1.name} is ambiguous; + s"""reference to ${outer1.name} is ambiguous; |$message |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) None @@ -1369,9 +1369,7 @@ trait Contexts { self: Analyzer => // At this point only one or the other of defSym and impSym might be set. if (defSym.exists) { - val ambiguity = - if (outerDefSym.exists) ambiguousWithEnclosing(outerDefSym, defSym, cx.enclClass.owner) - else None + val ambiguity = ambiguousWithEnclosing(outerDefSym, defSym, cx.enclClass.owner) ambiguity.getOrElse(finishDefSym(defSym, pre)) } else if (impSym.exists) { // If we find a competitor imp2 which imports the same name, possible outcomes are: diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index cb377ba343f2..ab03b5e81382 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -1,3 +1,6 @@ +t11921b.scala:156: error: could not find implicit value for parameter i: Int + def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) + ^ t11921b.scala:11: warning: reference to x is ambiguous; it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. @@ -47,6 +50,12 @@ To continue using the symbol from the superclass, write `this.a`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. val t = a // error ^ -error: No warnings can be incurred under -Xfatal-warnings. -7 warnings found +t11921b.scala:157: warning: reference to lo is ambiguous; +it is both defined in the enclosing object test10 and available in the enclosing class C as value lo (defined in class P) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.lo`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def v = t(lo) // error + ^ +8 warnings found one error found diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index 1d6f1ad24edb..eb79d23bb246 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -133,3 +133,27 @@ object test8 { } } } + +object test9 { + val lo: Int = 1 + class P { + implicit val lo: Int = 1 + } + class C extends P { + def t(implicit i: Int) = 10 + def u = t // ok, reference to `lo` by implicit search + def v = t(lo) // should warn, but doesn't. can't tell if reference to `lo` was explicit or not. + } +} + +object test10 { + implicit val lo: Int = 1 + class P { + val lo: Int = 1 + } + class C extends P { + def t(implicit i: Int) = 10 + def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) + def v = t(lo) // error + } +} From 772dc5c77299c65e3ab70b930b74c0558a6ff6da Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 27 Feb 2023 16:38:35 +0100 Subject: [PATCH 186/261] don't warn if the found symbol is implicit --- .../tools/nsc/typechecker/Contexts.scala | 44 ++++++++++--------- test/files/neg/t11921b.check | 13 +++++- test/files/neg/t11921b.scala | 24 ++++++++++ 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index e3217f0cdee1..5075a11f075b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1362,30 +1362,32 @@ trait Contexts { self: Analyzer => LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2") def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) = LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp") - def ambiguousDefinitions(sym: Symbol, other: Symbol, otherFoundInSuper: Boolean, otherEnclClass: Symbol) = { - if (otherFoundInSuper) { - val sym1 = sym.alternatives.head - val other1 = other.alternatives.head - val enclDesc = if (otherEnclClass.isAnonymousClass) "anonymous class" else otherEnclClass.toString - val parent = otherEnclClass.parentSymbols.find(_.isNonBottomSubClass(other1.owner)).getOrElse(NoSymbol) - val inherit = if (parent.exists && parent != other1.owner) s", inherited through parent $parent" else "" - val message = - s"""it is both defined in the enclosing ${sym1.owner} and available in the enclosing $enclDesc as $other1 (defined in ${other1.ownsString}$inherit) - |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |To continue using the symbol from the superclass, write `this.${sym1.name}`.""".stripMargin - if (currentRun.isScala3) - Some(LookupAmbiguous(message)) + def ambiguousDefinitions(outer: Symbol, inherited: Symbol, foundInSuper: Boolean, currentClass: Symbol) = + if (foundInSuper) { + if (inherited.isImplicit) None else { - // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning - other.updateAttachment(LookupAmbiguityWarning( - s"""reference to ${sym1.name} is ambiguous; - |$message - |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) - None + val outer1 = outer.alternatives.head + val inherited1 = inherited.alternatives.head + val classDesc = if (currentClass.isAnonymousClass) "anonymous class" else currentClass.toString + val parent = currentClass.parentSymbols.find(_.isNonBottomSubClass(inherited1.owner)).getOrElse(NoSymbol) + val inherit = if (parent.exists && parent != inherited1.owner) s", inherited through parent $parent" else "" + val message = + s"""it is both defined in the enclosing ${outer1.owner} and available in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) + |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. + |To continue using the symbol from the superclass, write `this.${outer1.name}`.""".stripMargin + if (currentRun.isScala3) + Some(LookupAmbiguous(message)) + else { + // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning + inherited.updateAttachment(LookupAmbiguityWarning( + s"""reference to ${outer1.name} is ambiguous; + |$message + |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) + None + } } } else - Some(LookupAmbiguous(s"it is both defined in ${sym.owner} and available as ${other.fullLocationString}")) - } + Some(LookupAmbiguous(s"it is both defined in ${outer.owner} and available as ${inherited.fullLocationString}")) def apply(thisContext: Context, name: Name)(qualifies: Symbol => Boolean): NameLookup = { lookupError = null diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 733629877624..19c100b7853d 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -1,3 +1,6 @@ +t11921b.scala:135: error: could not find implicit value for parameter i: Int + def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) + ^ t11921b.scala:11: warning: reference to x is ambiguous; it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. @@ -40,6 +43,12 @@ To continue using the symbol from the superclass, write `this.a`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. val t = a // error ^ -error: No warnings can be incurred under -Werror. -6 warnings +t11921b.scala:136: warning: reference to lo is ambiguous; +it is both defined in the enclosing object test10 and available in the enclosing class C as value lo (defined in class P) +Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. +To continue using the symbol from the superclass, write `this.lo`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + def v = t(lo) // error + ^ +7 warnings 1 error diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index ab2464b2ee9b..93447b5e0a5f 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -112,3 +112,27 @@ object test7 { } } } + +object test9 { + val lo: Int = 1 + class P { + implicit val lo: Int = 1 + } + class C extends P { + def t(implicit i: Int) = 10 + def u = t // ok, reference to `lo` by implicit search + def v = t(lo) // should warn, but doesn't. can't tell if reference to `lo` was explicit or not. + } +} + +object test10 { + implicit val lo: Int = 1 + class P { + val lo: Int = 1 + } + class C extends P { + def t(implicit i: Int) = 10 + def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) + def v = t(lo) // error + } +} From 5350bf3b36c5e0e76f1a3291ffb8c6fd3f6c1a2e Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 28 Feb 2023 14:42:52 +0100 Subject: [PATCH 187/261] clarify warning message --- .../tools/nsc/typechecker/Contexts.scala | 6 +-- test/files/neg/t11921-alias.check | 24 +++++----- test/files/neg/t11921b.check | 48 +++++++++---------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6321fb051b2b..629765cf7606 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -64,9 +64,9 @@ trait Contexts { self: Analyzer => val parent = currentClass.parentSymbols.find(_.isNonBottomSubClass(inherited1.owner)).getOrElse(NoSymbol) val inherit = if (parent.exists && parent != inherited1.owner) s", inherited through parent $parent" else "" val message = - s"""it is both defined in the enclosing ${outer1.owner} and available in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) - |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |To continue using the symbol from the superclass, write `this.${outer1.name}`.""".stripMargin + s"""it is both defined in the enclosing ${outer1.owner} and inherited in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) + |In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. + |Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.${outer1.name}`.""".stripMargin if (currentRun.isScala3) Some(LookupAmbiguous(message)) else { diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check index 241efa7decd3..f5193028ee7b 100644 --- a/test/files/neg/t11921-alias.check +++ b/test/files/neg/t11921-alias.check @@ -1,28 +1,28 @@ t11921-alias.scala:18: warning: reference to TT is ambiguous; -it is both defined in the enclosing object O and available in the enclosing class D as type TT (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.TT`. +it is both defined in the enclosing object O and inherited in the enclosing class D as type TT (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.TT`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n(x: TT) = x // ambiguous ^ t11921-alias.scala:38: warning: reference to c is ambiguous; -it is both defined in the enclosing class B and available in the enclosing anonymous class as value c (defined in class A) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.c`. +it is both defined in the enclosing class B and inherited in the enclosing anonymous class as value c (defined in class A) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.c`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n = c // ambiguous ^ t11921-alias.scala:57: warning: reference to name is ambiguous; -it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.name`. +it is both defined in the enclosing method m and inherited in the enclosing anonymous class as value name (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.name`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) ^ t11921-alias.scala:67: warning: reference to name is ambiguous; -it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.name`. +it is both defined in the enclosing method m and inherited in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.name`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) ^ diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index ab03b5e81382..ff39e0001fa2 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -2,58 +2,58 @@ t11921b.scala:156: error: could not find implicit value for parameter i: Int def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) ^ t11921b.scala:11: warning: reference to x is ambiguous; -it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.x`. +it is both defined in the enclosing object Test and inherited in the enclosing class D as value x (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ t11921b.scala:15: warning: reference to x is ambiguous; -it is both defined in the enclosing object Test and available in the enclosing anonymous class as value x (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.x`. +it is both defined in the enclosing object Test and inherited in the enclosing anonymous class as value x (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ t11921b.scala:26: warning: reference to y is ambiguous; -it is both defined in the enclosing method c and available in the enclosing anonymous class as value y (defined in class D) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.y`. +it is both defined in the enclosing method c and inherited in the enclosing anonymous class as value y (defined in class D) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.y`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ t11921b.scala:38: warning: reference to y is ambiguous; -it is both defined in the enclosing method c and available in the enclosing class E as value y (defined in class D) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.y`. +it is both defined in the enclosing method c and inherited in the enclosing class E as value y (defined in class D) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.y`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ t11921b.scala:65: warning: reference to global is ambiguous; -it is both defined in the enclosing package and available in the enclosing object D as value global (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.global`. +it is both defined in the enclosing package and inherited in the enclosing object D as value global (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.global`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(global) // error ^ t11921b.scala:75: warning: reference to x is ambiguous; -it is both defined in the enclosing object Uhu and available in the enclosing class C as value x (defined in class A, inherited through parent class B) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.x`. +it is both defined in the enclosing object Uhu and inherited in the enclosing class C as value x (defined in class A, inherited through parent class B) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def t = x // error, message mentions parent B ^ t11921b.scala:132: warning: reference to a is ambiguous; -it is both defined in the enclosing class C and available in the enclosing trait J as method a (defined in trait I) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.a`. +it is both defined in the enclosing class C and inherited in the enclosing trait J as method a (defined in trait I) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.a`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. val t = a // error ^ t11921b.scala:157: warning: reference to lo is ambiguous; -it is both defined in the enclosing object test10 and available in the enclosing class C as value lo (defined in class P) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.lo`. +it is both defined in the enclosing object test10 and inherited in the enclosing class C as value lo (defined in class P) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.lo`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def v = t(lo) // error ^ From d3737fbb92c67d8a6b3ab761db890cfbb6d17528 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 28 Feb 2023 15:04:57 +0100 Subject: [PATCH 188/261] clarify warning message --- .../tools/nsc/typechecker/Contexts.scala | 6 +-- test/files/neg/t11921-alias.check | 24 +++++------ test/files/neg/t11921.check | 6 +-- test/files/neg/t11921b.check | 42 +++++++++---------- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 5075a11f075b..a439b111a986 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1372,9 +1372,9 @@ trait Contexts { self: Analyzer => val parent = currentClass.parentSymbols.find(_.isNonBottomSubClass(inherited1.owner)).getOrElse(NoSymbol) val inherit = if (parent.exists && parent != inherited1.owner) s", inherited through parent $parent" else "" val message = - s"""it is both defined in the enclosing ${outer1.owner} and available in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) - |Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. - |To continue using the symbol from the superclass, write `this.${outer1.name}`.""".stripMargin + s"""it is both defined in the enclosing ${outer1.owner} and inherited in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) + |In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. + |Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.${outer1.name}`.""".stripMargin if (currentRun.isScala3) Some(LookupAmbiguous(message)) else { diff --git a/test/files/neg/t11921-alias.check b/test/files/neg/t11921-alias.check index a71a4f7a358c..7e9c5d043b2a 100644 --- a/test/files/neg/t11921-alias.check +++ b/test/files/neg/t11921-alias.check @@ -1,28 +1,28 @@ t11921-alias.scala:18: warning: reference to TT is ambiguous; -it is both defined in the enclosing object O and available in the enclosing class D as type TT (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.TT`. +it is both defined in the enclosing object O and inherited in the enclosing class D as type TT (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.TT`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n(x: TT) = x // ambiguous ^ t11921-alias.scala:38: warning: reference to c is ambiguous; -it is both defined in the enclosing class B and available in the enclosing anonymous class as value c (defined in class A) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.c`. +it is both defined in the enclosing class B and inherited in the enclosing anonymous class as value c (defined in class A) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.c`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def n = c // ambiguous ^ t11921-alias.scala:57: warning: reference to name is ambiguous; -it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.name`. +it is both defined in the enclosing method m and inherited in the enclosing anonymous class as value name (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.name`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) ^ t11921-alias.scala:67: warning: reference to name is ambiguous; -it is both defined in the enclosing method m and available in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.name`. +it is both defined in the enclosing method m and inherited in the enclosing anonymous class as value name (defined in class A, inherited through parent class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.name`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(name) ^ diff --git a/test/files/neg/t11921.check b/test/files/neg/t11921.check index 6a9275f1fab0..168bfa56963e 100644 --- a/test/files/neg/t11921.check +++ b/test/files/neg/t11921.check @@ -4,9 +4,9 @@ t11921.scala:6: error: type mismatch; def iterator = coll.iterator.map(f) // coll is ambiguous ^ t11921.scala:6: warning: reference to coll is ambiguous; -it is both defined in the enclosing method lazyMap and available in the enclosing anonymous class as method coll (defined in trait Iterable) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.coll`. +it is both defined in the enclosing method lazyMap and inherited in the enclosing anonymous class as method coll (defined in trait Iterable) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.coll`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def iterator = coll.iterator.map(f) // coll is ambiguous ^ diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 19c100b7853d..0e2e2391d349 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -2,51 +2,51 @@ t11921b.scala:135: error: could not find implicit value for parameter i: Int def u = t // doesn't compile in Scala 2 (maybe there's a ticket for that) ^ t11921b.scala:11: warning: reference to x is ambiguous; -it is both defined in the enclosing object Test and available in the enclosing class D as value x (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.x`. +it is both defined in the enclosing object Test and inherited in the enclosing class D as value x (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ t11921b.scala:15: warning: reference to x is ambiguous; -it is both defined in the enclosing object Test and available in the enclosing anonymous class as value x (defined in class C) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.x`. +it is both defined in the enclosing object Test and inherited in the enclosing anonymous class as value x (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(x) // error ^ t11921b.scala:26: warning: reference to y is ambiguous; -it is both defined in the enclosing method c and available in the enclosing anonymous class as value y (defined in class D) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.y`. +it is both defined in the enclosing method c and inherited in the enclosing anonymous class as value y (defined in class D) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.y`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ t11921b.scala:38: warning: reference to y is ambiguous; -it is both defined in the enclosing method c and available in the enclosing class E as value y (defined in class D) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.y`. +it is both defined in the enclosing method c and inherited in the enclosing class E as value y (defined in class D) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.y`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ t11921b.scala:75: warning: reference to x is ambiguous; -it is both defined in the enclosing object Uhu and available in the enclosing class C as value x (defined in class A, inherited through parent class B) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.x`. +it is both defined in the enclosing object Uhu and inherited in the enclosing class C as value x (defined in class A, inherited through parent class B) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.x`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def t = x // ambiguous, message mentions parent B ^ t11921b.scala:89: warning: reference to a is ambiguous; -it is both defined in the enclosing class C and available in the enclosing trait J as method a (defined in trait I) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.a`. +it is both defined in the enclosing class C and inherited in the enclosing trait J as method a (defined in trait I) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.a`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. val t = a // error ^ t11921b.scala:136: warning: reference to lo is ambiguous; -it is both defined in the enclosing object test10 and available in the enclosing class C as value lo (defined in class P) -Since Scala 3, symbols inherited from a superclass no longer shadow symbols defined in an outer scope. -To continue using the symbol from the superclass, write `this.lo`. +it is both defined in the enclosing object test10 and inherited in the enclosing class C as value lo (defined in class P) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.lo`. Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def v = t(lo) // error ^ From 5d5d00f45f9b3cca88bce016b5a330d94abc4e46 Mon Sep 17 00:00:00 2001 From: Kisaragi Marine Date: Sat, 7 Jan 2023 09:29:00 +0900 Subject: [PATCH 189/261] Patmat: Switch from ListSet to mutable.LinkedHashSet * experiment fix for SI-12499 (patmat perf) * use java.util.LinkedHashSet * switch j.u to s.c.m LinkedHashSet * avoid convert to Set to keep deterministic iteration order * fix compile error Co-authored-by: Dale Wijnand --- .../tools/nsc/transform/patmat/Logic.scala | 27 +- .../transform/patmat/MatchOptimization.scala | 2 +- .../tools/nsc/transform/patmat/Solving.scala | 8 +- test/files/neg/t12499.check | 8 + test/files/neg/t12499.scala | 591 ++++++++++++++++++ .../nsc/transform/patmat/SolvingTest.scala | 6 +- 6 files changed, 619 insertions(+), 23 deletions(-) create mode 100644 test/files/neg/t12499.check create mode 100644 test/files/neg/t12499.scala diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 67c6efba06a0..ec2c739f0c62 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -15,6 +15,7 @@ package tools.nsc.transform.patmat import scala.collection.mutable import scala.collection.immutable.ArraySeq +import scala.collection.IterableOps import scala.reflect.internal.util.Collections._ import scala.reflect.internal.util.HashSet @@ -113,24 +114,22 @@ trait Logic extends Debugging { def implications: List[(Sym, List[Sym], List[Sym])] } + // The error message of t7020 assumes the ops are ordered implicitly + // However, ListSet is slow (concatenate cost?), so use + // scala.collection.mutable.LinkedHashSet (which grantees "the order in which elements were inserted into the set") + // would be nice to statically check whether a prop is equational or pure, // but that requires typing relations like And(x: Tx, y: Ty) : (if(Tx == PureProp && Ty == PureProp) PureProp else Prop) - final case class And(ops: Set[Prop]) extends Prop + final case class And(ops: mutable.LinkedHashSet[Prop]) extends Prop object And { def apply(ps: Prop*) = create(ps) - def create(ps: Iterable[Prop]) = ps match { - case ps: Set[Prop] => new And(ps) - case _ => new And(ps.to(scala.collection.immutable.ListSet)) - } + def create(ps: Iterable[Prop]) = new And(ps.to(mutable.LinkedHashSet)) } - final case class Or(ops: Set[Prop]) extends Prop + final case class Or(ops: mutable.LinkedHashSet[Prop]) extends Prop object Or { def apply(ps: Prop*) = create(ps) - def create(ps: Iterable[Prop]) = ps match { - case ps: Set[Prop] => new Or(ps) - case _ => new Or(ps.to(scala.collection.immutable.ListSet)) - } + def create(ps: Iterable[Prop]) = new Or(ps.to(mutable.LinkedHashSet)) } final case class Not(a: Prop) extends Prop @@ -197,7 +196,7 @@ trait Logic extends Debugging { */ def simplify(f: Prop): Prop = { - def hasImpureAtom(ops0: collection.Iterable[Prop]): Boolean = { + def hasImpureAtom(ops0: Iterable[Prop]): Boolean = { // HOT method, imperative rewrite of: // ops.combinations(2).exists { // case Seq(a, Not(b)) if a == b => true @@ -247,7 +246,7 @@ trait Logic extends Debugging { } } - def mapConserve[A <: AnyRef](s: Set[A])(f: A => A): Set[A] = { + def mapConserve[CC[X] <: IterableOps[X, CC, CC[X]], A <: AnyRef](s: CC[A])(f: A => A): CC[A] = { var changed = false val s1 = s.map {a => val a1 = f(a) @@ -284,7 +283,7 @@ trait Logic extends Debugging { | (_: AtMostOne) => p } - def simplifyAnd(ps: Set[Prop]): Prop = { + def simplifyAnd(ps: Iterable[Prop]): Prop = { // recurse for nested And (pulls all Ands up) // build up Set in order to remove duplicates val props = mutable.LinkedHashSet.empty[Prop] @@ -300,7 +299,7 @@ trait Logic extends Debugging { else /\(props) } - def simplifyOr(ps: Set[Prop]): Prop = { + def simplifyOr(ps: Iterable[Prop]): Prop = { // recurse for nested Or (pulls all Ors up) // build up Set in order to remove duplicates val props = mutable.LinkedHashSet.empty[Prop] diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index 1274ca468669..6c320fd01e1d 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -56,7 +56,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchApproximation { val cond = test.prop def simplify(c: Prop): Set[Prop] = c match { - case And(ops) => ops flatMap simplify + case And(ops) => ops.flatMap(simplify).toSet case Or(ops) => Set(False) // TODO: make more precise case Not(Eq(Var(_), NullConst)) => Set.empty // not worth remembering case True => Set.empty // same diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala index dd6a524549dc..28b60686c5c1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala @@ -166,7 +166,7 @@ trait Solving extends Logic { } } - def and(bv: Set[Lit]): Lit = { + def and(bv: collection.Set[Lit]): Lit = { if (bv.isEmpty) { // this case can actually happen because `removeVarEq` could add no constraints constTrue @@ -178,14 +178,14 @@ trait Solving extends Logic { // op1 /\ op2 /\ ... /\ opx <==> // (o -> op1) /\ (o -> op2) ... (o -> opx) /\ (!op1 \/ !op2 \/... \/ !opx \/ o) // (!o \/ op1) /\ (!o \/ op2) ... (!o \/ opx) /\ (!op1 \/ !op2 \/... \/ !opx \/ o) - val new_bv = bv - constTrue // ignore `True` + val new_bv = bv.toSet - constTrue // ignore `True` val o = newLiteral() // auxiliary Tseitin variable new_bv.foreach(op => addClauseProcessed(clause(op, -o))) o } } - def or(bv: Set[Lit]): Lit = { + def or(bv: collection.Set[Lit]): Lit = { if (bv.isEmpty) { constFalse } else if (bv.size == 1) { @@ -196,7 +196,7 @@ trait Solving extends Logic { // op1 \/ op2 \/ ... \/ opx <==> // (op1 -> o) /\ (op2 -> o) ... (opx -> o) /\ (op1 \/ op2 \/... \/ opx \/ !o) // (!op1 \/ o) /\ (!op2 \/ o) ... (!opx \/ o) /\ (op1 \/ op2 \/... \/ opx \/ !o) - val new_bv = bv - constFalse // ignore `False` + val new_bv = bv.toSet - constFalse // ignore `False` val o = newLiteral() // auxiliary Tseitin variable addClauseProcessed(new_bv + (-o)) o diff --git a/test/files/neg/t12499.check b/test/files/neg/t12499.check new file mode 100644 index 000000000000..c49779c18ec4 --- /dev/null +++ b/test/files/neg/t12499.check @@ -0,0 +1,8 @@ +t12499.scala:455: warning: Cannot check match for unreachability. +The analysis required more space than allowed. +Please try with scalac -Ypatmat-exhaust-depth 60 or -Ypatmat-exhaust-depth off. + implicit val converter: Thing[Phantom.TypeA] => String = { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12499.scala b/test/files/neg/t12499.scala new file mode 100644 index 000000000000..91f397f12b24 --- /dev/null +++ b/test/files/neg/t12499.scala @@ -0,0 +1,591 @@ +// scalac: -Werror -Ystop-after:patmat -Ypatmat-exhaust-depth 30 +sealed trait Phantom[A] {} + +object Phantom { + type TypeA +} + +sealed abstract class ThingType(val v: Int) + +private object ThingType { + case object A extends ThingType(1) + case object B extends ThingType(-10) + case object C extends ThingType(-5) + case object D extends ThingType(15) + case object E extends ThingType(-16) +} + +sealed abstract class Thing[A](val thingType: ThingType) + +object Thing { + sealed abstract class ThingA[A] extends Thing[A](ThingType.A) + sealed abstract class ThingB[A] extends Thing[A](ThingType.B) + sealed abstract class ThingC[A] extends Thing[A](ThingType.C) + sealed abstract class ThingD[A] extends Thing[A](ThingType.D) + sealed abstract class ThingE[A] extends Thing[A](ThingType.E) +} + +object Stuff extends Phantom[Phantom.TypeA] { + import Phantom.TypeA + import Thing._ + + sealed abstract class OM(val id: String) extends ThingA[TypeA] { + def linkedA: ThingA[TypeA] + } + + case object OM1L extends ThingA[TypeA] + + case object OM1 extends OM("1") { + override def linkedA: ThingA[TypeA] = OM1L + } + + case object OM2L extends ThingA[TypeA] + + case object OM2 extends OM("2") { + override def linkedA: ThingA[TypeA] = OM2L + } + + case object OM3L extends ThingA[TypeA] + + case object OM3 extends OM("3") { + override def linkedA: ThingA[TypeA] = OM3L + } + + case object OM4L extends ThingA[TypeA] + + case object OM4 extends OM("4") { + override def linkedA: ThingA[TypeA] = OM4L + } + + case object OM5L extends ThingA[TypeA] + + case object OM5 extends OM("5") { + override def linkedA: ThingA[TypeA] = OM5L + } + + case object OM6L extends ThingA[TypeA] + + case object OM6 extends OM("6") { + override def linkedA: ThingA[TypeA] = OM6L + } + + case object OM7L extends ThingA[TypeA] + + case object OM7 extends OM("7") { + override def linkedA: ThingA[TypeA] = OM7L + } + + case object A1 extends ThingA[TypeA] + + case object A2 extends ThingA[TypeA] + + case object A3 extends ThingA[TypeA] + + sealed trait AA extends ThingA[TypeA] { + def linkedD: ThingD[TypeA] + } + + case object A4 extends AA { + override val linkedD: ThingD[TypeA] = A4L + } + + case object A5 extends AA { + override val linkedD: ThingD[TypeA] = A5L + } + + case object A6 extends AA { + override val linkedD: ThingD[TypeA] = A6L + } + + case object A7 extends AA { + override val linkedD: ThingD[TypeA] = A7L + } + + case object A8 extends AA { + override val linkedD: ThingD[TypeA] = A8L + } + + case object A9 extends AA { + override val linkedD: ThingD[TypeA] = A9L + } + + case object A10 extends AA { + override val linkedD: ThingD[TypeA] = A10L + } + + case object A11 extends AA { + override val linkedD: ThingD[TypeA] = A11L + } + + case object A12 extends AA { + override val linkedD: ThingD[TypeA] = A12L + } + + case object A13 extends AA { + override val linkedD: ThingD[TypeA] = A13L + } + + case object A14 extends AA { + override val linkedD: ThingD[TypeA] = A14L + } + + case object A15 extends AA { + override val linkedD: ThingD[TypeA] = A15L + } + + sealed abstract class G(val id: String) extends ThingA[TypeA] { + def linkedG: ThingA[TypeA] + } + + case object G1L extends ThingA[TypeA] + + case object G1 extends G("1") { + override def linkedG: ThingA[TypeA] = G1L + } + + case object G2L extends ThingA[TypeA] + + case object G2 extends G("2") { + override def linkedG: ThingA[TypeA] = G2L + } + + case object G3L extends ThingA[TypeA] + + case object G3 extends G("3") { + override def linkedG: ThingA[TypeA] = G3L + } + + case object G4L extends ThingA[TypeA] + + case object G4 extends G("4") { + override def linkedG: ThingA[TypeA] = G4L + } + + case object G5L extends ThingA[TypeA] + + case object G5 extends G("%") { + override def linkedG: ThingA[TypeA] = G5L + } + + case object G6L extends ThingA[TypeA] + + case object G6 extends G("6") { + override def linkedG: ThingA[TypeA] = G6L + } + + case object G7L extends ThingA[TypeA] + + case object G7 extends G("7") { + override def linkedG: ThingA[TypeA] = G7L + } + + case object G8L extends ThingA[TypeA] + + case object G8 extends G("8") { + override def linkedG: ThingA[TypeA] = G8L + } + + case object G9L extends ThingA[TypeA] + + case object G9 extends G("9") { + override def linkedG: ThingA[TypeA] = G9L + } + + case object G10L extends ThingA[TypeA] + + case object G10 extends G("10") { + override def linkedG: ThingA[TypeA] = G10L + } + + case object G11L extends ThingA[TypeA] + + case object G11 extends G("11") { + override def linkedG: ThingA[TypeA] = G11L + } + + sealed abstract class CC(val id: String) extends ThingA[TypeA] { + def c1: ThingA[TypeA] + + def c2: ThingA[TypeA] + + def c3: ThingA[TypeA] + + def c4: ThingD[TypeA] + + def c5: ThingD[TypeA] + } + + case object C1 extends CC("1") { + override def c1: ThingA[TypeA] = C11 + + override def c2: ThingA[TypeA] = C12 + + override def c3: ThingA[TypeA] = C13 + + override def c4: ThingD[TypeA] = C14 + + override def c5: ThingD[TypeA] = C15 + } + + case object C11 extends ThingA[TypeA] + + case object C12 extends ThingA[TypeA] + + case object C13 extends ThingA[TypeA] + + case object C2 extends CC("2") { + override def c1: ThingA[TypeA] = C21 + + override def c2: ThingA[TypeA] = C22 + + override def c3: ThingA[TypeA] = C23 + + override def c4: ThingD[TypeA] = C24 + + override def c5: ThingD[TypeA] = C25 + } + + case object C21 extends ThingA[TypeA] + + case object C22 extends ThingA[TypeA] + + case object C23 extends ThingA[TypeA] + + case object SN extends ThingC[TypeA] + + case object CLC extends ThingE[TypeA] + + case object SW extends ThingE[TypeA] + + case object A4L extends ThingD[TypeA] + + case object A5L extends ThingD[TypeA] + + case object A6L extends ThingD[TypeA] + + case object A7L extends ThingD[TypeA] + + case object A8L extends ThingD[TypeA] + + case object A9L extends ThingD[TypeA] + + case object A10L extends ThingD[TypeA] + + case object A11L extends ThingD[TypeA] + + case object A12L extends ThingD[TypeA] + + case object A13L extends ThingD[TypeA] + + case object A14L extends ThingD[TypeA] + + case object A15L extends ThingD[TypeA] + + case object ABC1 extends ThingD[TypeA] + + case object ABC2 extends ThingD[TypeA] + + case object ABC3 extends ThingD[TypeA] + + case object ABC4 extends ThingD[TypeA] + + case object ABC5 extends ThingD[TypeA] + + case object ABC6 extends ThingD[TypeA] + + case object ABC7 extends ThingD[TypeA] + + case object ABC8 extends ThingD[TypeA] + + case object ABC9 extends ThingD[TypeA] + + case object ABC10 extends ThingD[TypeA] + + case object C14 extends ThingD[TypeA] + + case object C15 extends ThingD[TypeA] + + case object C24 extends ThingD[TypeA] + + case object C25 extends ThingD[TypeA] + + case object ASD1 extends ThingD[TypeA] + + case object ASD2 extends ThingD[TypeA] + + case object ASD3 extends ThingD[TypeA] + + case object ASD4 extends ThingD[TypeA] + + case object ASD5 extends ThingE[TypeA] + + case object ASD6 extends ThingE[TypeA] + + sealed trait IR extends ThingE[TypeA] { + def linkedIR1: ThingD[TypeA] + def linkedIR2: ThingD[TypeA] + } + + case object IR11 extends ThingD[TypeA] + + case object IR12 extends ThingD[TypeA] + + case object IR1 extends IR { + override def linkedIR1: ThingD[TypeA] = IR11 + + override def linkedIR2: ThingD[TypeA] = IR12 + } + + case object IR21 extends ThingD[TypeA] + + case object IR22 extends ThingD[TypeA] + + case object IR2 extends IR { + override def linkedIR1: ThingD[TypeA] = IR21 + override def linkedIR2: ThingD[TypeA] = IR22 + } + + case object QW1 extends ThingE[TypeA] + + case object QW2 extends ThingE[TypeA] + + case object QW3 extends ThingE[TypeA] + + case object QW4 extends ThingE[TypeA] + + case object QW5 extends ThingE[TypeA] + + case object QW6 extends ThingE[TypeA] + + sealed abstract class IE(val id: String) extends ThingA[TypeA] { + def linkedIE1: ThingE[TypeA] + def linkedIE2: ThingE[TypeA] + def linkedIE3: Thing[TypeA] + def linkedIE4: Thing[TypeA] + } + + case object IE1 extends IE("1") { + override val linkedIE1: ThingE[TypeA] = IE11 + override val linkedIE2: ThingE[TypeA] = IE12 + override val linkedIE3: ThingD[TypeA] = ABC3 + override val linkedIE4: ThingD[TypeA] = ABC4 + } + + case object IE11 extends ThingE[TypeA] + + case object IE12 extends ThingE[TypeA] + + case object IE2 extends IE("2") { + override val linkedIE1: ThingE[TypeA] = IE21 + override val linkedIE2: ThingE[TypeA] = IE22 + override val linkedIE3: ThingE[TypeA] = IE23 + override val linkedIE4: ThingE[TypeA] = IE24 + } + + case object IE21 extends ThingE[TypeA] + + case object IE22 extends ThingE[TypeA] + + case object IE23 extends ThingE[TypeA] + + case object IE24 extends ThingE[TypeA] + + sealed abstract class LA extends ThingC[TypeA] + + case object LA1 extends LA + + case object LA2 extends LA + + case object LA3 extends LA + + case object LA4 extends LA + + case object LA5 extends ThingC[TypeA] + + sealed abstract class MAD(val id: String) extends ThingC[TypeA] { + def otherId: String + def linkedMAD1: ThingC[TypeA] + def linkedMAD2: ThingC[TypeA] + def linkedMAD3: ThingD[TypeA] + def linkedMAD4: ThingC[TypeA] + def linkedMAD5: ThingC[TypeA] + def linkedMAD6: ThingC[TypeA] + } + + case object MAD11 extends ThingC[TypeA] + + case object MAD12 extends ThingC[TypeA] + + case object MAD13 extends ThingD[TypeA] + + case object MAD14 extends ThingC[TypeA] + + case object MAD15 extends ThingC[TypeA] + + case object MAD16 extends ThingC[TypeA] + + case object MAD1 extends MAD("1") { + override def otherId: String = "c1" + override def linkedMAD1: ThingC[TypeA] = MAD11 + override def linkedMAD2: ThingC[TypeA] = MAD12 + override def linkedMAD3: ThingD[TypeA] = MAD13 + override def linkedMAD4: ThingC[TypeA] = MAD14 + override def linkedMAD5: ThingC[TypeA] = MAD15 + override def linkedMAD6: ThingC[TypeA] = MAD16 + } + + case object MAD21 extends ThingC[TypeA] + case object MAD22 extends ThingC[TypeA] + case object MAD23 extends ThingD[TypeA] + case object MAD24 extends ThingC[TypeA] + case object MAD25 extends ThingC[TypeA] + case object MAD26 extends ThingC[TypeA] + case object MAD2 extends MAD("2") { + override def otherId: String = "c2" + override def linkedMAD1: ThingC[TypeA] = MAD21 + override def linkedMAD2: ThingC[TypeA] = MAD22 + override def linkedMAD3: ThingD[TypeA] = MAD23 + override def linkedMAD4: ThingC[TypeA] = MAD24 + override def linkedMAD5: ThingC[TypeA] = MAD25 + override def linkedMAD6: ThingC[TypeA] = MAD26 + } +} + +object Matcher { + implicit val converter: Thing[Phantom.TypeA] => String = { + case Stuff.OM1 => "OM1" + case Stuff.OM1L => "OM1L" + case Stuff.OM2 => "OM2" + case Stuff.OM2L => "OM2L" + case Stuff.OM3 => "OM3" + case Stuff.OM3L => "OM3L" + case Stuff.OM4 => "OM4" + case Stuff.OM4L => "OM4L" + case Stuff.OM5 => "OM5" + case Stuff.OM5L => "OM5L" + case Stuff.OM6 => "OM6" + case Stuff.OM6L => "OM6L" + case Stuff.OM7 => "OM7" + case Stuff.OM7L => "OM7L" + case Stuff.A4 => "A4" + case Stuff.A5 => "A5" + case Stuff.A6 => "A6" + case Stuff.A7 => "A7" + case Stuff.A8 => "A8" + case Stuff.A9 => "A9" + case Stuff.A10 => "A10" + case Stuff.A11 => "A11" + case Stuff.A12 => "A12" + case Stuff.A13 => "A13" + case Stuff.A14 => "A14" + case Stuff.A15 => "A15" + case Stuff.A4L => "A4L" + case Stuff.A5L => "A5L" + case Stuff.A6L => "A6L" + case Stuff.A7L => "A7L" + case Stuff.A8L => "A8L" + case Stuff.A9L => "A9L" + case Stuff.A10L => "A10L" + case Stuff.A11L => "A11L" + case Stuff.A12L => "A12L" + case Stuff.A13L => "A13L" + case Stuff.A14L => "A14L" + case Stuff.A15L => "A15L" + case Stuff.ABC1 => "ABC1" + case Stuff.ABC2 => "ABC2" + case Stuff.ABC3 => "ABC3" + case Stuff.ABC4 => "ABC4" + case Stuff.QW1 => "QW1" + case Stuff.QW2 => "QW2" + case Stuff.IR1 => "IR1" + case Stuff.QW3 => "QW3" + case Stuff.QW4 => "QW4" + case Stuff.QW5 => "QW5" + case Stuff.QW6 => "QW6" + case Stuff.IE1 => "IE1" + case Stuff.IE11 => "IE11" + case Stuff.IE12 => "IE12" + case Stuff.IE2 => "IE2" + case Stuff.IE21 => "IE21" + case Stuff.IE22 => "IE22" + case Stuff.IE23 => "IE23" + case Stuff.IE24 => "IE24" + case Stuff.LA1 => "LA1" + case Stuff.LA2 => "LA2" + case Stuff.LA3 => "LA3" + case Stuff.LA5 => "LA5" + case Stuff.A3 => "A3" + case Stuff.ASD1 => "ASD1" + case Stuff.ASD5 => "ASD5" + case Stuff.ASD6 => "ASD6" + case Stuff.IR11 => "IR11" + case Stuff.IR12 => "IR12" + case Stuff.ASD2 => "ASD2" + case Stuff.ASD3 => "ASD3" + case Stuff.A1 => "A1" + case Stuff.A2 => "A2" + case Stuff.G1 => "G1" + case Stuff.G2 => "G2" + case Stuff.G3 => "G3" + case Stuff.G4 => "G4" + case Stuff.G5 => "G5" + case Stuff.G6 => "G6" + case Stuff.G1L => "G1L" + case Stuff.G2L => "G2L" + case Stuff.G3L => "G3L" + case Stuff.G4L => "G4L" + case Stuff.G5L => "G5L" + case Stuff.G6L => "G6L" + case Stuff.ABC5 => "ABC5" + case Stuff.ABC6 => "ABC6" + case Stuff.ABC7 => "ABC7" + case Stuff.ABC8 => "ABC8" + case Stuff.ABC9 => "ABC9" + case Stuff.ABC10 => "ABC10" + case Stuff.ASD4 => "ASD4" + case Stuff.SW => "SW" + case Stuff.C1 => "C1" + case Stuff.C11 => "C11" + case Stuff.IR2 => "IR2" + case Stuff.IR21 => "IR21" + case Stuff.IR22 => "IR22" + case Stuff.MAD14 => "MAD14" + case Stuff.MAD15 => "MAD15" + case Stuff.MAD11 => "MAD11" + case Stuff.MAD1 => "MAD1" + case Stuff.SN => "SN" + case Stuff.C12 => "C12" + case Stuff.C13 => "C13" + case Stuff.MAD12 => "MAD12" + case Stuff.C14 => "C14" + case Stuff.C15 => "C15" + case Stuff.G7 => "G7" + case Stuff.G7L => "G7L" + case Stuff.G8 => "G8" + case Stuff.G8L => "G8L" + case Stuff.C2 => "C2" + case Stuff.C21 => "C21" + case Stuff.C22 => "C22" + case Stuff.C23 => "C23" + case Stuff.C24 => "C24" + case Stuff.C25 => "C25" + case Stuff.MAD21 => "MAD21" + case Stuff.MAD22 => "MAD22" + case Stuff.MAD24 => "MAD24" + case Stuff.MAD25 => "MAD25" + case Stuff.MAD2 => "MAD2" + case Stuff.CLC => "CLC" + case Stuff.MAD13 => "MAD13" + case Stuff.MAD16 => "MAD16" + case Stuff.MAD23 => "MAD23" + case Stuff.MAD26 => "MAD26" + case Stuff.G9 => "G9" + case Stuff.G9L => "G9L" + case Stuff.LA4 => "LA4" + case Stuff.G10 => "G10" + case Stuff.G10L => "G10L" + case Stuff.G11 => "G11" + case Stuff.G11L => "G11L" + case _ => "unknown" + } +} diff --git a/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala b/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala index b5289263f390..40643b2af887 100644 --- a/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala +++ b/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala @@ -578,8 +578,8 @@ class SolvingTest { def pairWiseEncoding(ops: List[Sym]) = { And(ops.combinations(2).collect { - case a :: b :: Nil => Or(Not(a), Not(b)) - }.toSet[TestSolver.TestSolver.Prop]) + case a :: b :: Nil => Or(Not(a), Not(b)): Prop + }.to(mutable.LinkedHashSet)) } @Test @@ -593,5 +593,3 @@ class SolvingTest { assertEquals(expected.toSet, actual.toSet) } } - - From 5ce767fa2b295d0cb4ef56014ede18efbc90ea9c Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Sun, 5 Mar 2023 23:12:33 -0600 Subject: [PATCH 190/261] re-encrypt Travis-CI Sonatype secrets references scala/scala-dev#836 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09cac9c1c93c..67102ca1fc07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,8 +69,8 @@ env: - secure: "T1fxtvLTxioyXJYiC/zVYdNYsBOt+0Piw+xE04rB1pzeKahm9+G2mISdcAyqv6/vze9eIJt6jNHHpKX32/Z3Cs1/Ruha4m3k+jblj3S0SbxV6ht2ieJXLT5WoUPFRrU68KXI8wqUadXpjxeJJV53qF2FC4lhfMUsw1IwwMhdaE8=" # PRIVATE_REPO_PASS, for publishing to scala-ci Artifactory - secure: "dbAvl6KEuLwZ0MVQPZihFsPzCdiLbX0EFk3so+hcfEbksrmLQ1tn4X5ZM7Wy1UDR8uN9lxngEwHch7a7lKqpugzmXMew9Wnikr9WBWbJT77Z+XJ/jHI6YuiCRpRo+nvxXGp9Ry80tSIgx5eju0J83IaJL41BWlBkvyAd7YAHORI=" # GPG_SUBKEY_SECRET, so we can sign JARs - secure: "RTyzS6nUgthupw5M0fPwTlcOym1sWgBo8eXYepB2xGiQnRu4g583BGuNBW1UZ3vIjRETi/UKQ1HtMR+i7D8ptF1cNpomopncVJA1iy7pU2w0MJ0xgIPMuvtkIa3kxocd/AnxAp+UhUad3nC8lDpkvZsUhhyA0fb4iPKipd2b2xY=" # TRAVIS_TOKEN (login with GitHub as SethTisue), for triggering scala-dist job - - secure: "FvhicbSeys7VNTj9ZP/aNT0NhiQP/NNV0KRfK7IHxi3uOeaxFVfaQsln4lzqZn8dkntgzzNrE/VhvMIknfnISAPX7bShy6SRyj3V2BlcUpuem8WtwmkCaZ42xlCJteBL7NW0auG/8rxrNIAJXbRObqF+YdK6XsRMWaBMQHky+ss=" # SONA_USER, token username for publishing to Sonatype - - secure: "Y8CTlEdQbAS+P+LgkY05al/KSbccbX5BATm9N2GI9C6wH7oQuUU/VtU+bwvzeiF9DCsZPjrWXsa0JCuIQE+UzK1NWXxlkhUdGCaCBZ/nUecouBtMk2x/h7uIGpeYInxA041r5SuBecZuZQI79nhl+BwZSLu82Vy1QtP0/Cd8oRM=" # SONA_PASS, token password for publishing to Sonatype + - secure: "PbDzgRGivsDM/1P18dIAZiZnK8yG+fxU/9Ho6DkAd8pvsu7S08MPks+ekM0uSVeKxYj7Npzd3XTe4weEXM7Jtljy3CRHoPasI0TF/6ZVOb7H+MMP1cg9K1xrZXKfEk2RABCbMxKtrEv9BDa/lVtjCCEKWAIPz38Z6q2mKk417Ps=" # SONA_USER, token username for publishing to Sonatype + - secure: "D/V5nrAJsAc6t5ZMoeSt37ViIsJyRmagA286M3zWn/uZhgk4mbgYfzu6rDbYeUTBB9jX8YHKPtzUrxqcnlpkV8z6USAbDhzYSLL/QqcLnTjKZZ3KvPEimNQIXX8Nb1KIrlXNQ/xTE8u+GNvQLDdxa60QqlzvA3tt5vnVl3GatFE=" # SONA_PASS, token password for publishing to Sonatype # caching for sdkman / sbt / ivy / coursier imported from scala-dev cache: From 4fd2a2e0200cc5f42d0c873bef208c45557f3fef Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 6 Mar 2023 08:11:22 -0600 Subject: [PATCH 191/261] new reference compiler is 2.12.18-M1, for JDK 20 --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index 4d906bd1bd78..4f3b87bec6ed 100644 --- a/versions.properties +++ b/versions.properties @@ -1,5 +1,5 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.12.17 +starr.version=2.12.18-M1 # The scala.binary.version determines how modules are resolved. It is set as follows: # - After 2.x.0 is released, the binary version is 2.x From 1357841f149a988b9fe57d982d3c0ad69000cf80 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 6 Mar 2023 11:13:48 -0600 Subject: [PATCH 192/261] get tests passing on JDK 20 --- build.sbt | 1 + test/files/jvm/future-spec.check | 3 -- test/files/jvm/future-spec/FutureTests.scala | 3 +- test/files/jvm/future-spec/PromiseTests.scala | 5 +-- test/files/jvm/future-spec/TryTests.scala | 1 + test/files/jvm/future-spec/main.scala | 3 +- test/files/jvm/scala-concurrent-tck.check | 1 - test/files/jvm/scala-concurrent-tck.scala | 12 +++++- test/files/jvm/unreachable/Foo_1.check | 38 +++++++++---------- test/files/neg/macro-exception.check | 6 +++ test/files/neg/macro-invalidret.check | 6 +++ .../run/reflection-magicsymbols-invoke.check | 3 ++ test/files/run/t2318.scala | 5 ++- test/files/run/t6989.check | 6 +++ test/files/run/t9529.check | 19 +++++++++- test/files/run/t9529/Test_1.scala | 4 +- .../junit/scala/runtime/FloatBoxingTest.scala | 2 +- .../nsc/classpath/MultiReleaseJarTest.scala | 3 +- .../scala/tools/testing/AllocationTest.scala | 10 +---- test/scaladoc/run/doc-source-url-java.scala | 8 ++-- test/scaladoc/run/doc-source-url.scala | 8 ++-- 21 files changed, 90 insertions(+), 57 deletions(-) diff --git a/build.sbt b/build.sbt index c9adff9075e3..688cc11ed55f 100644 --- a/build.sbt +++ b/build.sbt @@ -810,6 +810,7 @@ lazy val test = project //scalacOptions in Compile += "-Yvalidate-pos:parser,typer", (Compile / scalacOptions) -= "-Ywarn-unused:imports", (IntegrationTest / javaOptions) ++= List("-Xmx2G", "-Dpartest.exec.in.process=true", "-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US") ++ addOpensForTesting, + IntegrationTest / javaOptions ++= { if (scala.util.Properties.isJavaAtLeast("18")) List("-Djava.security.manager=allow") else Nil }, (IntegrationTest / testOptions) += Tests.Argument("-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US"), testFrameworks += new TestFramework("scala.tools.partest.sbt.Framework"), (IntegrationTest / testOptions) += Tests.Argument(s"""-Dpartest.java_opts=-Xmx1024M -Xms64M ${addOpensForTesting.mkString(" ")}"""), diff --git a/test/files/jvm/future-spec.check b/test/files/jvm/future-spec.check index 7c7bcd4cc190..e69de29bb2d1 100644 --- a/test/files/jvm/future-spec.check +++ b/test/files/jvm/future-spec.check @@ -1,3 +0,0 @@ -warning: one deprecation (since 2.11.0) -warning: 19 deprecations (since 2.12.0) -warning: 20 deprecations in total; re-run with -deprecation for details diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index 15968ad7f0b8..e8b3f80166f0 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -5,8 +5,7 @@ import scala.collection._ import scala.runtime.NonLocalReturnControl import scala.util.{Try,Success,Failure} - - +@annotation.nowarn("cat=deprecation") class FutureTests extends MinimalScalaTest { /* some utils */ diff --git a/test/files/jvm/future-spec/PromiseTests.scala b/test/files/jvm/future-spec/PromiseTests.scala index 67c8c542ba58..19730bbfcec5 100644 --- a/test/files/jvm/future-spec/PromiseTests.scala +++ b/test/files/jvm/future-spec/PromiseTests.scala @@ -1,6 +1,3 @@ - - - import scala.concurrent._ import scala.concurrent.duration._ import scala.concurrent.duration.Duration.Inf @@ -8,7 +5,7 @@ import scala.collection._ import scala.runtime.NonLocalReturnControl import scala.util.{Try,Success,Failure} - +@annotation.nowarn("cat=deprecation") class PromiseTests extends MinimalScalaTest { import ExecutionContext.Implicits._ diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala index 01bb3c9d369a..ca968aa8d9e0 100644 --- a/test/files/jvm/future-spec/TryTests.scala +++ b/test/files/jvm/future-spec/TryTests.scala @@ -5,6 +5,7 @@ import scala.util.{Try,Success,Failure} +@annotation.nowarn("cat=deprecation") class TryTests extends MinimalScalaTest { class MyException extends Exception val e = new Exception("this is an exception") diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index f5db78e30b11..a9a12be6dabb 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -1,5 +1,4 @@ - - +// scalac: -deprecation import scala.collection._ import scala.concurrent._ diff --git a/test/files/jvm/scala-concurrent-tck.check b/test/files/jvm/scala-concurrent-tck.check index ae3cc2136d14..e69de29bb2d1 100644 --- a/test/files/jvm/scala-concurrent-tck.check +++ b/test/files/jvm/scala-concurrent-tck.check @@ -1 +0,0 @@ -warning: 75 deprecations (since 2.12.0); re-run with -deprecation for details diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index b296f1c04d14..ea5ae3dba6f2 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -1,3 +1,5 @@ +// scalac: -deprecation + import scala.concurrent.{ Future, Promise, @@ -12,8 +14,9 @@ import scala.util.{ Try, Success, Failure } import scala.concurrent.duration.Duration import scala.reflect.{ classTag, ClassTag } import scala.tools.partest.TestUtil.intercept -import scala.annotation.tailrec +import scala.annotation.{nowarn, tailrec} +@nowarn("cat=deprecation") trait TestBase { trait Done { def apply(proof: => Boolean): Unit } def once(body: Done => Unit) { @@ -28,7 +31,7 @@ trait TestBase { } } - +@nowarn("cat=deprecation") trait FutureCallbacks extends TestBase { import ExecutionContext.Implicits._ @@ -123,6 +126,7 @@ trait FutureCallbacks extends TestBase { } +@nowarn("cat=deprecation") trait FutureCombinators extends TestBase { import ExecutionContext.Implicits._ @@ -519,6 +523,7 @@ def testTransformFailure(): Unit = once { } +@nowarn("cat=deprecation") trait FutureProjections extends TestBase { import ExecutionContext.Implicits._ @@ -700,6 +705,7 @@ trait BlockContexts extends TestBase { testPopCustom() } +@nowarn("cat=deprecation") trait Promises extends TestBase { import ExecutionContext.Implicits._ @@ -739,6 +745,7 @@ trait Exceptions extends TestBase { trait GlobalExecutionContext extends TestBase { import ExecutionContext.Implicits._ + @nowarn("cat=deprecation") // Thread.getID is deprecated since JDK 19 def testNameOfGlobalECThreads(): Unit = once { done => Future({ val expectedName = "scala-execution-context-global-"+ Thread.currentThread.getId @@ -749,6 +756,7 @@ trait GlobalExecutionContext extends TestBase { testNameOfGlobalECThreads() } +@nowarn("cat=deprecation") // Thread.getID is deprecated since JDK 19 trait CustomExecutionContext extends TestBase { import scala.concurrent.{ ExecutionContext, Awaitable } diff --git a/test/files/jvm/unreachable/Foo_1.check b/test/files/jvm/unreachable/Foo_1.check index 4e41284c58a0..57824245009d 100644 --- a/test/files/jvm/unreachable/Foo_1.check +++ b/test/files/jvm/unreachable/Foo_1.check @@ -1,8 +1,8 @@ java.lang.ClassNotFoundException: Test - at java.net.URLClassLoader.findClass(URLClassLoader.java:382) - at java.lang.ClassLoader.loadClass(ClassLoader.java:419) - at java.lang.ClassLoader.loadClass(ClassLoader.java:352) - at scala.tools.partest.nest.Runner.$anonfun$execTestInProcess$2(Runner.scala:251) + at java.net.URLClassLoader.findClass(URLClassLoader.java:387) + at java.lang.ClassLoader.loadClass(ClassLoader.java:418) + at java.lang.ClassLoader.loadClass(ClassLoader.java:351) + at scala.tools.partest.nest.Runner.$anonfun$execTestInProcess$2(Runner.scala:252) at scala.util.DynamicVariable.withValue(DynamicVariable.scala:62) at scala.Console$.withOut(Console.scala:167) at scala.tools.partest.nest.StreamCapture$.$anonfun$capturingOutErr$2(StreamCapture.scala:44) @@ -11,26 +11,26 @@ java.lang.ClassNotFoundException: Test at scala.tools.partest.nest.StreamCapture$.$anonfun$capturingOutErr$1(StreamCapture.scala:43) at scala.tools.partest.nest.StreamCapture$.savingSystem(StreamCapture.scala:22) at scala.tools.partest.nest.StreamCapture$.capturingOutErr(StreamCapture.scala:38) - at scala.tools.partest.nest.Runner.$anonfun$execTestInProcess$1(Runner.scala:250) + at scala.tools.partest.nest.Runner.$anonfun$execTestInProcess$1(Runner.scala:251) at scala.tools.partest.nest.StreamCapture$.withExtraProperties(StreamCapture.scala:68) - at scala.tools.partest.nest.Runner.run$2(Runner.scala:246) - at scala.tools.partest.nest.Runner.$anonfun$execTestInProcess$3(Runner.scala:273) + at scala.tools.partest.nest.Runner.run$2(Runner.scala:247) + at scala.tools.partest.nest.Runner.$anonfun$execTestInProcess$3(Runner.scala:274) at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23) at scala.tools.partest.nest.TrapExit$.apply(TrapExit.scala:28) - at scala.tools.partest.nest.Runner.execTestInProcess(Runner.scala:273) - at scala.tools.partest.nest.Runner.exec$1(Runner.scala:675) - at scala.tools.partest.nest.Runner.$anonfun$runRunTest$1(Runner.scala:677) + at scala.tools.partest.nest.Runner.execTestInProcess(Runner.scala:274) + at scala.tools.partest.nest.Runner.exec$1(Runner.scala:703) + at scala.tools.partest.nest.Runner.$anonfun$runRunTest$1(Runner.scala:705) at scala.tools.partest.TestState.andAlso(TestState.scala:33) - at scala.tools.partest.nest.Runner.$anonfun$runTestCommon$1(Runner.scala:577) - at scala.tools.partest.nest.Runner.runInContext(Runner.scala:438) - at scala.tools.partest.nest.Runner.runTestCommon(Runner.scala:574) - at scala.tools.partest.nest.Runner.runRunTest(Runner.scala:677) - at scala.tools.partest.nest.Runner.run(Runner.scala:666) - at scala.tools.partest.nest.AbstractRunner.liftedTree1$1(AbstractRunner.scala:310) - at scala.tools.partest.nest.AbstractRunner.runTest(AbstractRunner.scala:310) - at scala.tools.partest.nest.AbstractRunner.$anonfun$runTestsForFiles$2(AbstractRunner.scala:335) + at scala.tools.partest.nest.Runner.$anonfun$runTestCommon$1(Runner.scala:605) + at scala.tools.partest.nest.Runner.runInContext(Runner.scala:439) + at scala.tools.partest.nest.Runner.runTestCommon(Runner.scala:605) + at scala.tools.partest.nest.Runner.runRunTest(Runner.scala:705) + at scala.tools.partest.nest.Runner.run(Runner.scala:694) + at scala.tools.partest.nest.AbstractRunner.liftedTree1$1(AbstractRunner.scala:317) + at scala.tools.partest.nest.AbstractRunner.runTest(AbstractRunner.scala:317) + at scala.tools.partest.nest.AbstractRunner.$anonfun$runTestsForFiles$2(AbstractRunner.scala:342) at scala.tools.partest.package$$anon$2.call(package.scala:141) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) - at java.lang.Thread.run(Thread.java:748) + at java.lang.Thread.run(Thread.java:750) diff --git a/test/files/neg/macro-exception.check b/test/files/neg/macro-exception.check index dca97aebce70..d2249d9b9ea9 100644 --- a/test/files/neg/macro-exception.check +++ b/test/files/neg/macro-exception.check @@ -1,6 +1,12 @@ Test_2.scala:2: error: exception during macro expansion: java.lang.Exception at Macros$.impl(Macros_1.scala:6) +#partest java20+ + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:578) + at scala.reflect.macros.runtime.JavaReflectionRuntimes$JavaReflectionResolvers.$anonfun$resolveJavaReflectionRuntime$6(JavaReflectionRuntimes.scala:51) + at scala.tools.nsc.typechecker.Macros.macroExpandWithRuntime(Macros.scala:849) +#partest Macros.exception ^ diff --git a/test/files/neg/macro-invalidret.check b/test/files/neg/macro-invalidret.check index 68842c44d476..286f556c9d3c 100644 --- a/test/files/neg/macro-invalidret.check +++ b/test/files/neg/macro-invalidret.check @@ -18,6 +18,12 @@ Macros_Test_2.scala:7: error: macro defs must have explicitly specified return t Macros_Test_2.scala:15: error: exception during macro expansion: java.lang.NullPointerException at Impls$.foo3(Impls_1.scala:8) +#partest java20+ + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) + at java.base/java.lang.reflect.Method.invoke(Method.java:578) + at scala.reflect.macros.runtime.JavaReflectionRuntimes$JavaReflectionResolvers.$anonfun$resolveJavaReflectionRuntime$6(JavaReflectionRuntimes.scala:51) + at scala.tools.nsc.typechecker.Macros.macroExpandWithRuntime(Macros.scala:849) +#partest foo3 ^ diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index cca4151e49fb..2a74b2b6f865 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -51,6 +51,9 @@ method notify: ()Unit method notifyAll: ()Unit method synchronized: [T0](x$1: T0)T0 method toString: ()String +#partest java20+ +method wait0: (x$1: Long)Unit +#partest method wait: ()Unit method wait: (x$1: Long)Unit method wait: (x$1: Long, x$2: Int)Unit diff --git a/test/files/run/t2318.scala b/test/files/run/t2318.scala index 824954f10103..ea1f5b784381 100644 --- a/test/files/run/t2318.scala +++ b/test/files/run/t2318.scala @@ -22,14 +22,15 @@ object Test { } def t1() = { - val p = Runtime.getRuntime().exec("ls"); + val p = Runtime.getRuntime().exec(Array("ls")); type Destroyable = { def destroy() : Unit } def doDestroy( obj : Destroyable ) : Unit = obj.destroy(); doDestroy( p ); } @deprecated def t2() = { - System.setSecurityManager(Mgr) + if (!scala.util.Properties.isJavaAtLeast("18")) + System.setSecurityManager(Mgr) val b = new Bar { def bar = println("bar") } b.bar diff --git a/test/files/run/t6989.check b/test/files/run/t6989.check index baa118e1e5df..2dba3c323de2 100644 --- a/test/files/run/t6989.check +++ b/test/files/run/t6989.check @@ -107,12 +107,14 @@ isProtected = false isPublic = true privateWithin = ============ +#partest !java20+ sym = value this$0, signature = foo.JavaClass_1, owner = class $PrivateJavaClass isPrivate = false isProtected = false isPublic = false privateWithin = package foo ============ +#partest sym = class $PrivateJavaClass, signature = JavaClass_1.this.$PrivateJavaClass.type, owner = class JavaClass_1 isPrivate = true isProtected = false @@ -131,12 +133,14 @@ isProtected = false isPublic = true privateWithin = ============ +#partest !java20+ sym = value this$0, signature = foo.JavaClass_1, owner = class $ProtectedJavaClass isPrivate = false isProtected = false isPublic = false privateWithin = package foo ============ +#partest sym = class $ProtectedJavaClass, signature = JavaClass_1.this.$ProtectedJavaClass.type, owner = class JavaClass_1 isPrivate = false isProtected = false @@ -155,12 +159,14 @@ isProtected = false isPublic = true privateWithin = ============ +#partest !java20+ sym = value this$0, signature = foo.JavaClass_1, owner = class $PublicJavaClass isPrivate = false isProtected = false isPublic = false privateWithin = package foo ============ +#partest sym = class $PublicJavaClass, signature = JavaClass_1.this.$PublicJavaClass.type, owner = class JavaClass_1 isPrivate = false isProtected = false diff --git a/test/files/run/t9529.check b/test/files/run/t9529.check index 38ad198f56ba..666a758485b0 100644 --- a/test/files/run/t9529.check +++ b/test/files/run/t9529.check @@ -32,7 +32,7 @@ u: List(@anns.Ann_0$Container(value={@anns.Ann_0(name="u", value="you"), @anns.A List(@anns.Ann_0$Container(value={@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) -#partest java15+ +#partest java17 A: List() B: List(@java.lang.Deprecated(forRemoval=false, since="")) C: List(@anns.Ann_0(name="C", value="see")) @@ -49,3 +49,20 @@ u: List(@anns.Ann_0$Container({@anns.Ann_0(name="u", value="you"), @anns.Ann_0(n List(@anns.Ann_0$Container({@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) +#partest java20+ +A: List() +B: List(@java.lang.Deprecated(forRemoval=false, since="")) +C: List(@anns.Ann_0(name="C", value="see")) +D: List(@anns.Ann_0.Container({@anns.Ann_0(name="D", value="dee"), @anns.Ann_0(name="D", value="dye")})) + +x: List(@anns.Ann_0(name="x", value="eks")) +y: List(@anns.Ann_0.Container({@anns.Ann_0(name="y", value="why"), @anns.Ann_0(name="y", value="wye")})) + +t: List(@anns.Ann_0(name="t", value="tee")) +u: List(@anns.Ann_0.Container({@anns.Ann_0(name="u", value="you"), @anns.Ann_0(name="u", value="yew")})) + +1: List(@anns.Ann_0(name="1", value="one")) +2: List(@anns.Ann_0.Container({@anns.Ann_0(name="2", value="two"), @anns.Ann_0(name="2", value="tew")})) + +List(@anns.Ann_0.Container({@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) + diff --git a/test/files/run/t9529/Test_1.scala b/test/files/run/t9529/Test_1.scala index 5df64f9c89a9..92530b1fb2c7 100644 --- a/test/files/run/t9529/Test_1.scala +++ b/test/files/run/t9529/Test_1.scala @@ -19,7 +19,7 @@ class Test @Ann_0(name = "", value = "constructor") @Ann_0(name = "" ) = () // todo: annotations on types - // todo? annotaitons on packages + // todo? annotations on packages } object Test extends App { @@ -56,4 +56,4 @@ object Test extends App { def anns(ae: AnnotatedElement) = ae.getAnnotations.toList.filterNot(_.isInstanceOf[reflect.ScalaSignature]) def prints(l: List[String]) = { println(l mkString "\n") ; println() } -} \ No newline at end of file +} diff --git a/test/junit/scala/runtime/FloatBoxingTest.scala b/test/junit/scala/runtime/FloatBoxingTest.scala index 4298cd7980bd..64fe8242f1d5 100644 --- a/test/junit/scala/runtime/FloatBoxingTest.scala +++ b/test/junit/scala/runtime/FloatBoxingTest.scala @@ -47,7 +47,7 @@ class FloatBoxingTest extends SideEffectTest with AllocationTest { assertEquals(57, nonAllocating(value.byteValue())) } @Test def str: Unit = { - val cost = allocationInfo(java.lang.Double.toString(value), "", false) + val cost = allocationInfo(java.lang.Float.toString(value), "", false) assertEquals("12345.0", exactAllocates(cost.min)(value.toString())) } @Test def hash1: Unit = { diff --git a/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala b/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala index 3e11d281bb24..0f26e8ae3f58 100644 --- a/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala +++ b/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala @@ -64,7 +64,8 @@ class MultiReleaseJarTest extends BytecodeTesting { } try { Assert.assertTrue(lookup("java.lang.invoke.LambdaMetafactory", "8")) - Assert.assertFalse(lookup("java.lang.invoke.LambdaMetafactory", "7")) + if (!Properties.isJavaAtLeast("20")) + Assert.assertFalse(lookup("java.lang.invoke.LambdaMetafactory", "7")) Assert.assertTrue(lookup("java.lang.invoke.LambdaMetafactory", "9")) } finally { cleanup.close() diff --git a/test/junit/scala/tools/testing/AllocationTest.scala b/test/junit/scala/tools/testing/AllocationTest.scala index 1dbd8642f215..934a977fba09 100644 --- a/test/junit/scala/tools/testing/AllocationTest.scala +++ b/test/junit/scala/tools/testing/AllocationTest.scala @@ -152,13 +152,5 @@ trait AllocationTest { case class AllocationExecution(executionCount: Int = 1000, warmupCount: Int = 1000) case class AllocationInfo[T](result: T, allocations: Array[Long]) { - def min: Long = { - var min = allocations(0) - var i = 1 - while (i < allocations.length) { - min = Math.min(min, allocations(i)) - i += i - } - min - } + def min = allocations.min } diff --git a/test/scaladoc/run/doc-source-url-java.scala b/test/scaladoc/run/doc-source-url-java.scala index 4c323d41d17b..ad4a2123fb72 100644 --- a/test/scaladoc/run/doc-source-url-java.scala +++ b/test/scaladoc/run/doc-source-url-java.scala @@ -10,7 +10,7 @@ * additional information regarding copyright ownership. */ -import java.net.URL +import java.net.URI import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Universe @@ -26,14 +26,14 @@ object Test extends ScaladocModelTest { override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile))) - def scaladocSettings = "-doc-source-url file:€{FILE_PATH}||€{FILE_EXT}||€{FILE_PATH_EXT}||€{FILE_LINE}" + def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}" def testModel(rootPackage: Package) = { import access._ val clazz = rootPackage._class("WithSource") - val expect = s"file:test/scaladoc/resources/doc-source-url||.java||test/scaladoc/resources/doc-source-url.java||13" - assert(clazz.sourceUrl.contains(new URL(expect)), s"got ${clazz.sourceUrl}") + val expect = s"file:test/scaladoc/resources/doc-source-url@@.java@@test/scaladoc/resources/doc-source-url.java@@13" + assert(clazz.sourceUrl.contains(new URI(expect).toURL), s"got ${clazz.sourceUrl}") } } diff --git a/test/scaladoc/run/doc-source-url.scala b/test/scaladoc/run/doc-source-url.scala index 2d104722075d..d994e83e3f55 100644 --- a/test/scaladoc/run/doc-source-url.scala +++ b/test/scaladoc/run/doc-source-url.scala @@ -10,7 +10,7 @@ * additional information regarding copyright ownership. */ -import java.net.URL +import java.net.URI import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Universe @@ -26,14 +26,14 @@ object Test extends ScaladocModelTest { override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile))) - def scaladocSettings = "-doc-source-url file:€{FILE_PATH}||€{FILE_EXT}||€{FILE_PATH_EXT}||€{FILE_LINE}" + def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}" def testModel(rootPackage: Package) = { import access._ val clazz = rootPackage._class("WithSource") - val expect = s"file:test/scaladoc/resources/doc-source-url||.scala||test/scaladoc/resources/doc-source-url.scala||13" - assert(clazz.sourceUrl.contains(new URL(expect)), s"got ${clazz.sourceUrl}") + val expect = s"file:test/scaladoc/resources/doc-source-url@@.scala@@test/scaladoc/resources/doc-source-url.scala@@13" + assert(clazz.sourceUrl.contains(new URI(expect).toURL), s"got ${clazz.sourceUrl}") } } From 25303693343bb8031e8a9ce91c0c90fa2f0c78bb Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Fri, 10 Feb 2023 15:00:21 -0800 Subject: [PATCH 193/261] add JDK 20 (early access) to daily CI matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44967a1ccd31..82fdcdd0877e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - java: [8, 11, 17] + java: [8, 11, 17, 20-ea] runs-on: ${{matrix.os}} steps: - run: git config --global core.autocrlf false From 5871b73c17bfb913ecc250afb096e632c9282a0b Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 6 Mar 2023 10:30:34 -0600 Subject: [PATCH 194/261] new reference compiler is 2.13.11-M1, for JDK 20 --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index b491938bf301..9bd91efa4226 100644 --- a/versions.properties +++ b/versions.properties @@ -1,5 +1,5 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.13.10 +starr.version=2.13.11-M1 # These are the versions of the modules that go with this release. # Artifact dependencies: From 258a13bab73a70858549e6cbc1c1ec9757fbcc3f Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 6 Mar 2023 11:58:50 -0600 Subject: [PATCH 195/261] get tests passing on JDK 20 --- build.sbt | 6 ++++++ .../nsc/classpath/DirectoryClassPath.scala | 4 ++-- .../classpath/VirtualDirectoryClassPath.scala | 4 ++-- .../scala/tools/nsc/util/ClassPath.scala | 4 ++-- src/library/scala/io/Source.scala | 2 +- src/library/scala/util/Using.scala | 1 + src/library/scala/util/control/NonFatal.scala | 1 + .../util/AbstractFileClassLoader.scala | 9 +++++++-- .../tools/nsc/interpreter/jline/Reader.scala | 4 ++-- .../scala/tools/nsc/interpreter/Power.scala | 6 +++--- .../tools/nsc/doc/model/ModelFactory.scala | 2 +- test/files/jvm/scala-concurrent-tck.scala | 5 +++-- .../run/reflection-magicsymbols-invoke.check | 3 +++ test/files/run/t6989.check | 6 +++--- test/files/run/t9529.check | 19 ++++++++++++++++++- .../junit/scala/runtime/FloatBoxingTest.scala | 2 +- .../nsc/classpath/MultiReleaseJarTest.scala | 3 ++- test/junit/scala/util/TryTest.scala | 2 ++ test/junit/scala/util/UsingTest.scala | 1 + test/scaladoc/run/doc-source-url-java.scala | 8 ++++---- test/scaladoc/run/doc-source-url.scala | 8 ++++---- 21 files changed, 69 insertions(+), 31 deletions(-) diff --git a/build.sbt b/build.sbt index 3cdf66f8e3b1..e2739024c148 100644 --- a/build.sbt +++ b/build.sbt @@ -135,6 +135,12 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories compileOrder := CompileOrder.JavaThenScala, projectFolder := thisProject.value.id, // overridden in configureAsSubproject Compile / javacOptions ++= Seq("-g", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked"), + Compile / javacOptions ++= ( + if (scala.util.Properties.isJavaAtLeast("20")) + Seq("-Xlint:-options") // allow `-source 1.8` and `-target 1.8` + else + Seq()), + Compile / javacOptions ++= Seq("-g", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked"), Compile / unmanagedJars := Seq.empty, // no JARs in version control! Compile / sourceDirectory := baseDirectory.value, Compile / unmanagedSourceDirectories := List(baseDirectory.value), diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala index 2f4cd0941477..4dd8743c5f4e 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -13,7 +13,7 @@ package scala.tools.nsc.classpath import java.io.{Closeable, File} -import java.net.URL +import java.net.{URI, URL} import scala.reflect.io.{AbstractFile, PlainFile, PlainNioFile} import scala.tools.nsc.util.{ClassPath, ClassRepresentation, EfficientClassPath} @@ -209,7 +209,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No if (inPackage.isRoot) ClassPathEntries(packages(inPackage), Nil) else ClassPathEntries(packages(inPackage), classes(inPackage)) - def asURLs: Seq[URL] = Seq(new URL("jrt:/")) + def asURLs: Seq[URL] = Seq(new URI("jrt:/").toURL) // We don't yet have a scheme to represent the JDK modules in our `-classpath`. // java models them as entries in the new "module path", we'll probably need to follow this. def asClassPathStrings: Seq[String] = Nil diff --git a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala index f8d626841473..ac187762c095 100644 --- a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala @@ -15,7 +15,7 @@ package scala.tools.nsc.classpath import scala.tools.nsc.util.ClassRepresentation import scala.reflect.io.{AbstractFile, VirtualDirectory} import FileUtils._ -import java.net.URL +import java.net.{URI, URL} import scala.reflect.internal.util.AbstractFileClassLoader import scala.tools.nsc.util.ClassPath @@ -35,7 +35,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi def isPackage(f: AbstractFile): Boolean = f.isPackage // mimic the behavior of the old nsc.util.DirectoryClassPath - def asURLs: Seq[URL] = Seq(new URL("file://_VIRTUAL_/" + dir.name)) + def asURLs: Seq[URL] = Seq(new URI("file://_VIRTUAL_/" + dir.name).toURL) def asClassPathStrings: Seq[String] = Seq(dir.path) override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 520d10c73e9e..94bdbaaa0fc7 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -15,7 +15,7 @@ package util import io.{AbstractFile, Directory, File, Jar} import java.net.MalformedURLException -import java.net.URL +import java.net.{URI, URL} import java.util.regex.PatternSyntaxException import File.pathSeparator @@ -184,7 +184,7 @@ object ClassPath { } def specToURL(spec: String): Option[URL] = - try Some(new URL(spec)) + try Some(new URI(spec).toURL) catch { case _: MalformedURLException => None } def manifests: List[java.net.URL] = { diff --git a/src/library/scala/io/Source.scala b/src/library/scala/io/Source.scala index 4ccb419e856e..02f8d50dbf3c 100644 --- a/src/library/scala/io/Source.scala +++ b/src/library/scala/io/Source.scala @@ -132,7 +132,7 @@ object Source { /** same as fromURL(new URL(s)) */ def fromURL(s: String)(implicit codec: Codec): BufferedSource = - fromURL(new URL(s))(codec) + fromURL(new URI(s).toURL)(codec) /** same as fromInputStream(url.openStream())(Codec(enc)) */ diff --git a/src/library/scala/util/Using.scala b/src/library/scala/util/Using.scala index bf0ed0cfbe34..e90c651b471c 100644 --- a/src/library/scala/util/Using.scala +++ b/src/library/scala/util/Using.scala @@ -226,6 +226,7 @@ object Using { } private def preferentiallySuppress(primary: Throwable, secondary: Throwable): Throwable = { + @annotation.nowarn("cat=deprecation") // avoid warning on mention of ThreadDeath def score(t: Throwable): Int = t match { case _: VirtualMachineError => 4 case _: LinkageError => 3 diff --git a/src/library/scala/util/control/NonFatal.scala b/src/library/scala/util/control/NonFatal.scala index eb5d82fcd511..3bce19e8b2be 100644 --- a/src/library/scala/util/control/NonFatal.scala +++ b/src/library/scala/util/control/NonFatal.scala @@ -36,6 +36,7 @@ object NonFatal { /** * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal */ + @annotation.nowarn("cat=deprecation") // avoid warning on mention of ThreadDeath def apply(t: Throwable): Boolean = t match { // VirtualMachineError includes OutOfMemoryError and other fatal errors case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala index 2c50d5cf9443..28062740a315 100644 --- a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala @@ -16,7 +16,7 @@ package reflect.internal.util import scala.collection.mutable import scala.collection.immutable.ArraySeq import scala.reflect.io.AbstractFile -import java.net.{URL, URLConnection, URLStreamHandler} +import java.net.{URI, URL, URLConnection, URLStreamHandler} import java.security.cert.Certificate import java.security.{CodeSource, ProtectionDomain} import java.util.{Collections => JCollections, Enumeration => JEnumeration} @@ -76,6 +76,10 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) else defineClass(name, bytes, 0, bytes.length, protectionDomain) } + + // on JDK 20 the URL constructor we're using is deprecated, but the recommended + // replacement, URL.of, doesn't exist on JDK 8 + @annotation.nowarn("cat=deprecation") override protected def findResource(name: String): URL = findAbstractFile(name) match { case null => null case file => new URL(null, s"memory:${file.path}", new URLStreamHandler { @@ -85,6 +89,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) } }) } + override protected def findResources(name: String): JEnumeration[URL] = findResource(name) match { case null => JCollections.enumeration(JCollections.emptyList[URL]) //JCollections.emptyEnumeration[URL] case url => JCollections.enumeration(JCollections.singleton(url)) @@ -98,7 +103,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) val n = s.lastIndexOf('!') if (n < 0) null else { val path = s.substring(0, n) - new ProtectionDomain(new CodeSource(new URL(path), null.asInstanceOf[Array[Certificate]]), null, this, null) + new ProtectionDomain(new CodeSource(new URI(path).toURL, null.asInstanceOf[Array[Certificate]]), null, this, null) } } } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala index 6f3f518205a9..0bc37ec1f374 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala @@ -21,7 +21,7 @@ import org.jline.reader.impl.{CompletionMatcherImpl, DefaultParser, LineReaderIm import org.jline.terminal.Terminal import java.io.{ByteArrayInputStream, File} -import java.net.{MalformedURLException, URL} +import java.net.{MalformedURLException, URI, URL} import java.util.{List => JList} import scala.io.Source import scala.reflect.internal.Chars @@ -80,7 +80,7 @@ object Reader { sys.props .get("jline.inputrc") .flatMap { path => - try Some(new URL(path)) + try Some(new URI(path).toURL) catch { case _: MalformedURLException => Some(new File(path).toURI.toURL) diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index 0d65f8bcf5f5..a0199a407220 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -13,7 +13,7 @@ package scala.tools.nsc.interpreter import java.io.InputStream -import java.net.URL +import java.net.{URI, URL} import scala.collection.mutable import scala.io.Codec @@ -248,9 +248,9 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re class RichReplString(s: String) { // make an url out of the string def u: URL = ( - if (s contains ":") new URL(s) + if (s contains ":") new URI(s).toURL else if (new java.io.File(s).exists) new java.io.File(s).toURI.toURL - else new URL("http://" + s) + else new URI("http://" + s).toURL ) } class RichInputStream(in: InputStream)(implicit codec: Codec) { diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index 88c5b39c8dec..7f44270a7128 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -331,7 +331,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case "TPL_NAME" => tplName } val patchedString = patches.replaceAllIn(settings.docsourceurl.value, m => java.util.regex.Matcher.quoteReplacement(substitute(m.group(1))) ) - new java.net.URL(patchedString) + new java.net.URI(patchedString).toURL } else None } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 3633582cb376..f31d8b2a5757 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -9,7 +9,7 @@ import scala.concurrent.{ Awaitable, blocking } -import scala.annotation.tailrec +import scala.annotation.{nowarn, tailrec} import scala.concurrent.duration._ import scala.reflect.{classTag, ClassTag} import scala.tools.testkit.AssertUtil.{Fast, Slow, assertThrows, waitFor, waitForIt} @@ -864,7 +864,8 @@ class Exceptions extends TestBase { class GlobalExecutionContext extends TestBase { import ExecutionContext.Implicits._ - + + @nowarn("cat=deprecation") // Thread.getID is deprecated since JDK 19 def testNameOfGlobalECThreads(): Unit = once { done => Future({ val expectedName = "scala-execution-context-global-"+ Thread.currentThread.getId diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index 6759edfecff3..88a692e26e73 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -51,6 +51,9 @@ method notify: (): Unit method notifyAll: (): Unit method synchronized: [T0](x$1: T0): T0 method toString: (): String +#partest java20+ +method wait0: (x$1: Long): Unit +#partest method wait: (): Unit method wait: (x$1: Long): Unit method wait: (x$1: Long, x$2: Int): Unit diff --git a/test/files/run/t6989.check b/test/files/run/t6989.check index a51db3067d30..3537181b2d7f 100644 --- a/test/files/run/t6989.check +++ b/test/files/run/t6989.check @@ -107,7 +107,7 @@ isProtected = false isPublic = true privateWithin = ============ -#partest !java18 +#partest !java18+ sym = value this$0, signature = foo.JavaClass_1, owner = class $PrivateJavaClass isPrivate = false isProtected = false @@ -133,7 +133,7 @@ isProtected = false isPublic = true privateWithin = ============ -#partest !java18 +#partest !java18+ sym = value this$0, signature = foo.JavaClass_1, owner = class $ProtectedJavaClass isPrivate = false isProtected = false @@ -159,7 +159,7 @@ isProtected = false isPublic = true privateWithin = ============ -#partest !java18 +#partest !java18+ sym = value this$0, signature = foo.JavaClass_1, owner = class $PublicJavaClass isPrivate = false isProtected = false diff --git a/test/files/run/t9529.check b/test/files/run/t9529.check index 38ad198f56ba..666a758485b0 100644 --- a/test/files/run/t9529.check +++ b/test/files/run/t9529.check @@ -32,7 +32,7 @@ u: List(@anns.Ann_0$Container(value={@anns.Ann_0(name="u", value="you"), @anns.A List(@anns.Ann_0$Container(value={@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) -#partest java15+ +#partest java17 A: List() B: List(@java.lang.Deprecated(forRemoval=false, since="")) C: List(@anns.Ann_0(name="C", value="see")) @@ -49,3 +49,20 @@ u: List(@anns.Ann_0$Container({@anns.Ann_0(name="u", value="you"), @anns.Ann_0(n List(@anns.Ann_0$Container({@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) +#partest java20+ +A: List() +B: List(@java.lang.Deprecated(forRemoval=false, since="")) +C: List(@anns.Ann_0(name="C", value="see")) +D: List(@anns.Ann_0.Container({@anns.Ann_0(name="D", value="dee"), @anns.Ann_0(name="D", value="dye")})) + +x: List(@anns.Ann_0(name="x", value="eks")) +y: List(@anns.Ann_0.Container({@anns.Ann_0(name="y", value="why"), @anns.Ann_0(name="y", value="wye")})) + +t: List(@anns.Ann_0(name="t", value="tee")) +u: List(@anns.Ann_0.Container({@anns.Ann_0(name="u", value="you"), @anns.Ann_0(name="u", value="yew")})) + +1: List(@anns.Ann_0(name="1", value="one")) +2: List(@anns.Ann_0.Container({@anns.Ann_0(name="2", value="two"), @anns.Ann_0(name="2", value="tew")})) + +List(@anns.Ann_0.Container({@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) + diff --git a/test/junit/scala/runtime/FloatBoxingTest.scala b/test/junit/scala/runtime/FloatBoxingTest.scala index 4a4adbc2ee94..ffe8ca3b560e 100644 --- a/test/junit/scala/runtime/FloatBoxingTest.scala +++ b/test/junit/scala/runtime/FloatBoxingTest.scala @@ -45,7 +45,7 @@ class FloatBoxingTest extends SideEffectTest with AllocationTest { assertEquals(57, nonAllocating(value.byteValue())) } @Test def str(): Unit = { - val cost = allocationInfo(java.lang.Double.toString(value)) + val cost = allocationInfo(java.lang.Float.toString(value)) assertEquals("12345.0", exactAllocates(cost.min)(value.toString())) } @Test def hash1(): Unit = { diff --git a/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala b/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala index 542408f6b1cd..0736e53cdf05 100644 --- a/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala +++ b/test/junit/scala/tools/nsc/classpath/MultiReleaseJarTest.scala @@ -64,7 +64,8 @@ class MultiReleaseJarTest extends BytecodeTesting { } try { Assert.assertTrue(lookup("java.lang.invoke.LambdaMetafactory", "8")) - Assert.assertFalse(lookup("java.lang.invoke.LambdaMetafactory", "7")) + if (!Properties.isJavaAtLeast("20")) + Assert.assertFalse(lookup("java.lang.invoke.LambdaMetafactory", "7")) Assert.assertTrue(lookup("java.lang.invoke.LambdaMetafactory", "9")) } finally { cleanup.close() diff --git a/test/junit/scala/util/TryTest.scala b/test/junit/scala/util/TryTest.scala index c505abd77596..4981a789f63e 100644 --- a/test/junit/scala/util/TryTest.scala +++ b/test/junit/scala/util/TryTest.scala @@ -64,6 +64,7 @@ class TryTest { assertEquals(Failure(e), Failure[Int](e).map(_ => throw e2)) } + @deprecated("ThreadDeath is deprecated on JDK 20", "") @Test def `map when there is a fatal exception`(): Unit = { val e3 = new ThreadDeath assertThrows[ThreadDeath] { @@ -82,6 +83,7 @@ class TryTest { val e2 = new Exception assertEquals(Failure(e), Failure[Int](e).flatMap[Int](_ => throw e2)) } + @deprecated("ThreadDeath is deprecated on JDK 20", "") @Test def `flatMap when there is a fatal exception`(): Unit = { val e3 = new ThreadDeath assertThrows[ThreadDeath] { diff --git a/test/junit/scala/util/UsingTest.scala b/test/junit/scala/util/UsingTest.scala index f68830934b57..cad0e00ade12 100644 --- a/test/junit/scala/util/UsingTest.scala +++ b/test/junit/scala/util/UsingTest.scala @@ -7,6 +7,7 @@ import scala.annotation.unused import scala.reflect.ClassTag import scala.util.control.ControlThrowable +@deprecated("ThreadDeath is deprecated on JDK 20", "") class UsingTest { import UsingTest._ diff --git a/test/scaladoc/run/doc-source-url-java.scala b/test/scaladoc/run/doc-source-url-java.scala index 714d79e73d73..a131e9c00360 100644 --- a/test/scaladoc/run/doc-source-url-java.scala +++ b/test/scaladoc/run/doc-source-url-java.scala @@ -10,7 +10,7 @@ * additional information regarding copyright ownership. */ -import java.net.URL +import java.net.URI import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Universe @@ -26,14 +26,14 @@ object Test extends ScaladocModelTest { override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile))) - def scaladocSettings = "-doc-source-url file:€{FILE_PATH}||€{FILE_EXT}||€{FILE_PATH_EXT}||€{FILE_LINE}" + def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}" def testModel(rootPackage: Package) = { import access._ val clazz = rootPackage._class("WithSource") - val expect = s"file:test/scaladoc/resources/doc-source-url||.java||test/scaladoc/resources/doc-source-url.java||13" - assert(clazz.sourceUrl.contains(new URL(expect)), s"got ${clazz.sourceUrl}") + val expect = s"file:test/scaladoc/resources/doc-source-url@@.java@@test/scaladoc/resources/doc-source-url.java@@13" + assert(clazz.sourceUrl.contains(new URI(expect).toURL), s"got ${clazz.sourceUrl}") } } diff --git a/test/scaladoc/run/doc-source-url.scala b/test/scaladoc/run/doc-source-url.scala index ce1abc3b0b36..0811fd25ddfe 100644 --- a/test/scaladoc/run/doc-source-url.scala +++ b/test/scaladoc/run/doc-source-url.scala @@ -10,7 +10,7 @@ * additional information regarding copyright ownership. */ -import java.net.URL +import java.net.URI import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Universe @@ -26,14 +26,14 @@ object Test extends ScaladocModelTest { override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile))) - def scaladocSettings = "-doc-source-url file:€{FILE_PATH}||€{FILE_EXT}||€{FILE_PATH_EXT}||€{FILE_LINE}" + def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}" def testModel(rootPackage: Package) = { import access._ val clazz = rootPackage._class("WithSource") - val expect = s"file:test/scaladoc/resources/doc-source-url||.scala||test/scaladoc/resources/doc-source-url.scala||13" - assert(clazz.sourceUrl.contains(new URL(expect)), s"got ${clazz.sourceUrl}") + val expect = s"file:test/scaladoc/resources/doc-source-url@@.scala@@test/scaladoc/resources/doc-source-url.scala@@13" + assert(clazz.sourceUrl.contains(new URI(expect).toURL), s"got ${clazz.sourceUrl}") } } From 39762a76811e5f29ca9da41c6fcd8366402b430f Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Fri, 10 Feb 2023 15:00:21 -0800 Subject: [PATCH 196/261] add JDK 20 (early access) to daily CI matrix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80a6f33b18b7..72d98fbdab65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - java: [8, 11, 17] + java: [8, 11, 17, 20-ea] runs-on: ${{matrix.os}} steps: - run: git config --global core.autocrlf false From 45dad1e01f6ae9eff8deee45846463c7171625a0 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 9 Mar 2023 16:13:06 -0800 Subject: [PATCH 197/261] update .gitignore --- .gitignore | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index bda4862d1afe..364aebc66430 100644 --- a/.gitignore +++ b/.gitignore @@ -46,15 +46,18 @@ **/.cache /.idea /.settings +metals.sbt # Standard symbolic link to build/quick/bin /qbin # sbt's target directories -/target/ -/project/target/ -/project/project/target/ -/project/project/project/target/ +target/ /build-sbt/ local.sbt -jitwatch.out \ No newline at end of file +jitwatch.out + +# metals +.metals/ +.bloop/ +.bsp/ From 30158dd90b4a9094280107517955f48d4485bd41 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 9 Mar 2023 16:20:11 -0800 Subject: [PATCH 198/261] fix (?) REPL test that intermittently fails on Windows the test was rewritten on the 2.13.x branch and (although I'm not positive) I don't remember seeing it fail there, so this backports the 2.13 version, fingers crossed that will put an end to the intermittent failures references scala/scala-dev#831 --- test/files/run/repl-paste-parse.check | 3 ++- test/files/run/repl-paste-parse.scala | 32 ++++++++++----------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/test/files/run/repl-paste-parse.check b/test/files/run/repl-paste-parse.check index 7b2148dc747f..e7871c1dc755 100755 --- a/test/files/run/repl-paste-parse.check +++ b/test/files/run/repl-paste-parse.check @@ -1,4 +1,5 @@ -Type in expressions for evaluation. Or try :help. + +scala> scala> repl-paste-parse.script:1: error: illegal start of simple pattern val case = 9 diff --git a/test/files/run/repl-paste-parse.scala b/test/files/run/repl-paste-parse.scala index 80b7f5922546..9f10c9abe68b 100644 --- a/test/files/run/repl-paste-parse.scala +++ b/test/files/run/repl-paste-parse.scala @@ -1,27 +1,19 @@ - -import java.io.{ BufferedReader, StringReader, StringWriter, PrintWriter } - -import scala.tools.partest.DirectTest -import scala.tools.nsc.interpreter.ILoop +import scala.tools.partest.ReplTest +import scala.tools.nsc.Settings import scala.tools.nsc.GenericRunnerSettings +import scala.tools.nsc.settings.MutableSettings -object Test extends DirectTest { - override def extraSettings = s"-usejavacp -i $scriptPath" +object Test extends ReplTest { def scriptPath = testPath.changeExtension("script") - override def newSettings(args: List[String]) = { - val ss = new GenericRunnerSettings(Console.println) - ss.processArguments(args, true) - ss + override def transformSettings(s: Settings) = s match { + case m: MutableSettings => + val t = new GenericRunnerSettings(s.errorFn) + m copyInto t + t processArgumentString s"-usejavacp -i $scriptPath" + t + case _ => s } + def code = "" - def show() = { - val r = new BufferedReader(new StringReader("")) - val w = new StringWriter - val p = new PrintWriter(w, true) - new ILoop(r, p).process(settings) - w.toString.linesIterator foreach { s => - if (!s.startsWith("Welcome to Scala")) println(s) - } - } } From ef6db2b629c8b23f1276ecdbd2d588f4f75fa1e7 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Fri, 10 Mar 2023 19:37:40 +0000 Subject: [PATCH 199/261] Update sbt-buildinfo to 0.11.0 in 2.12.x --- project/project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/project/plugins.sbt b/project/project/plugins.sbt index 22366532fc04..71982a81514a 100644 --- a/project/project/plugins.sbt +++ b/project/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") From f69089cb7d1c5f3e3dfb89e00b15d8d2d554c895 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 6 Mar 2023 12:50:49 -0800 Subject: [PATCH 200/261] javabootclasspath supersedes jrt --- .../scala/tools/util/PathResolver.scala | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index a26219b6606b..dbaa6d6f9bba 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -16,12 +16,13 @@ package util import java.net.URL +import scala.reflect.io.{Directory, File, Path} import scala.tools.reflect.WrappedProperties.AccessControl import scala.tools.nsc.{CloseableRegistry, Settings} +import scala.tools.nsc.classpath._ import scala.tools.nsc.util.ClassPath -import scala.reflect.io.{Directory, File, Path} +import scala.util.Properties.isJavaAtLeast import PartialFunction.condOpt -import scala.tools.nsc.classpath._ // Loosely based on the draft specification at: // https://wiki.scala-lang.org/display/SIW/Classpath @@ -211,22 +212,21 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr private val classPathFactory = new ClassPathFactory(settings, closeableRegistry) - import PathResolver.{ AsLines, Defaults, ppcp } + import PathResolver.{AsLines, Defaults, ppcp} - private def cmdLineOrElse(name: String, alt: String) = { - (commandLineFor(name) match { - case Some("") => None - case x => x - }) getOrElse alt - } + private def cmdLineOrElse(name: String, alt: String) = + commandLineFor(name) match { + case Some("") | None => alt + case Some(x) => x + } private def commandLineFor(s: String): Option[String] = condOpt(s) { - case "javabootclasspath" => settings.javabootclasspath.value - case "javaextdirs" => settings.javaextdirs.value - case "bootclasspath" => settings.bootclasspath.value - case "extdirs" => settings.extdirs.value - case "classpath" | "cp" => settings.classpath.value - case "sourcepath" => settings.sourcepath.value + case "javabootclasspath" => settings.javabootclasspath.value + case "javaextdirs" => settings.javaextdirs.value + case "bootclasspath" => settings.bootclasspath.value + case "extdirs" => settings.extdirs.value + case "classpath" | "cp" => settings.classpath.value + case "sourcepath" => settings.sourcepath.value } /** Calculated values based on any given command line options, falling back on @@ -258,8 +258,10 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr // Assemble the elements! def basis = List[Iterable[ClassPath]]( - jrt, // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available) - classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. + if (!settings.javabootclasspath.isSetByUser && isJavaAtLeast(9)) + jrt // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available) + else + classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. classesInPath(scalaBootClassPath), // 4. The Scala boot class path. From 698de29d8c60b496c54388fbf39cfefd1a4dd062 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 13 Mar 2023 17:40:27 +0800 Subject: [PATCH 201/261] Add an icon for IDEA to display https://www.scala-lang.org/resources/img/frontpage/scala-spiral.png --- .idea/icon.png | Bin 0 -> 80003 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .idea/icon.png diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8280fd4bfc3fdcec03961d30dc51fe2caaff1b1e GIT binary patch literal 80003 zcmZs@bx<7b6E%uMaCb@25Xj>05Zpot?(Xg^?yd>$B!L77?zY%21P`{jJImrOm%P94 zyH&U9{;{=HQ`56OPj{c^obH*8{ir67gGq^rfPjFbsPN$v0sw@uUf|;G9#xlLRL*uD-A+B zr3~u%FL}%vM02V*1Coa;X}pSy2u_ovZx@KQP44uIBGyJBG z$o|=Tq2G-?mYGRuI+9GIBhTZ?_%Ucz0Q@lCc(t>>P3=yT_S|}XH!cLZ0SkchJVxAR zS8NIc`EwhqL~4#fZOGNVG`?F;_p9z_?-GDGe#@a==(99XN4mfZF$Y8gBC~%7jK7Mw zHVHjW=!1;E$~U8R{ ztQdb<%pJXg%=(x*a995O)e3ub+UG5n;H{r!UDxW~Ha0FMFDAc|oN>JWnSAbeYF~D~ zzDisPszP-fjsEA6@5~WsPU396``5qU;y)}JjeVCczVE>;tqn*fj!CN*@ zVR3WMKic@>$Ov5K;GxS)=*@D?$DqSNblRuA<>yq)xz$}9ox z>-jOL^g8&$NnOJGPNLlR@%G7G!-VI~NGJH!{_SXc1Yg~M-isZB1mO33Wop&OPmtx~ zqd;ScNM&S_`hckd%$=qnqHcb3*es2%&l*(ZBvB&p8ZR%<=HA3vZTzWC>GNX@nXLVx z3-nrqtLu88z4}z`2pU@bH23fP%T8pI_T6KXWoNY~ht5NP7x)J<_HnJMtf>e$4T~ji z@c&&T<4GFdo349tPjBz-?oV2GyRfWgt%mRdA0txLNbDYcP@U1C$A7`@p}84j5kDnT zH)(wkXj)u0(SFmmRi`C8EE~&(Oq`xq`1mI?CAAv$h(xquX67Y>u3s{!>wd+{%I8u- z>&+e8J8>6RsRPxY(%W{)Mm1+*oZXvg1it`=%K`%n)&I*nfq<7)Z7l@C=VykW*PYvM zm2>NtbI)d#oX=*5pWe!iYfXIa&FKA(Iot9Q$&*t3SO4y|$Y;%=@L@jLNzLcE7IVzx z<4mii5d7E0I-MNMN7v>K+|kWXEWP;@S`DyYXA@bJuOqEF>bo=imnKvV$nE#=?rjJ4 zKy`_bw#P%8_IeX?S-D7v)5TBCAO|Md?urG-*5N>vaOEi9(VWW$uzD8KhJ1)?_>3EH zI8_*QU7uNaJN%9>*vY2}s7|hF*(T^U-u@nJ)Nz+T+iG4`=XJabk}G#Wq%x?*{V$jU za0GZ$SJ3UgM8#^-U8dzD+dIQM?$m1~zvgMw;jp0H{tufTvK3oR8lmT8*){`AoI8Wk z$);WKytU1N7OwED4^*KqN zzh#$>@?`1ADo2Rr8n%ovfjcohEg-J_cPNEIRYsinC+Rn(UZJHN1=x;bu1@~~aYDHi zy4>ydOhp&4c0qO-cs;&PGsbaP7Yy?HiJ2I$SiKm`@V38rOjfSwKvSe#TGNea(m1rv z`<-A8o>*Hf+eBSq_cPPL5f;@>!{v3T72WLaOY#1s`Irh7zp)WNFJAt-Y~!`6RpWiF z{3BSf(*)ZP<5!F?GA<_O02oyU^!>z_-j|G-y`mf&kad`^Wxo{0MV*fpg%e__>}G!C zJ(0ZN9TI8(KVRQ1zaZ34lbf}n{VSe3s=oV6ak=WzMTkV)F`pKf!m=Pcsg_2AD*oq3k85}J*@Jj04XJ1G0+b><4&`qYo?`$&mYSLO3RCOhg z1nVL`cUhXu4rtYW4$#fCz=M!?#$2tEfJxT~1XiC5asF1($~&3n2gr_F)w4i)4B6vH zmTymw`ie=&HUX8!wOcgG-k`n9+JBh~Sqep$c)k^Yb)Apit5p{S9$2Bu}r}E$$mSD1$j@Bo$`HNcm9SBg>7M zq^PtKIpLrmOqbPc9r%(azve@$mx7<$o65>0stc(m)Z1MozOw}OA^1Ss$LEB_PTakUQjKx z;^pNT-QDaTUx+XFBp((M1$}6~#Ie4{nJe$cJttZ2KH0Mw)su2g>Xf+68Dklf)__Lsc5szCpXYOKn+j99kPGv(D5W* zbF}=jp{#8$ghn5CKePNc4!jaKT1!PgnuZc(&S3?kp%t=GUU49BCiWS~_LtnqqYuKP;yenK{a+LA zA;JtHH$W* zhrV5(-IBAhK_5Xpb?{|nysr4&CnZ=3XM2HVx)%xAv73=489K;+lq~;tY2v5euhSTk zf{i^NV*sI(fptrbaw-V zz~`@1Fvta))$t_kG3)obSZ@7j@-1^_bJz4{ekclY%onCMe+a_Is`zLY($jLRL{80$ z+ELHH$gICZYLY6k7T=N%Ue%WA-GLL zl$n1hbo>jF;pSKRC;zGirHI^p&dTJP`lo^A1|UoWJH9mj^R<(}U7XzrE3-YUGk*S) z<2UvZP-3eiq@{OuX}!HV5<0M9bHm9;UHSDDZCJPufo#*0IOmnk3%VIvpm*IW7y9mA z7cMOYy4!g)0~VIsgnh7rGL4((Rgiv9eM0}NnmX!_tQ|U!75}$1=^9FmTA{I*)R>F{di)0x>4qTia7!=btIC@QLwtTzDxhM}J7JTLCSTdN@J$$unG8`p)rq{P*!O zn_npGm4f%0=#Zud+1|^RSDWhqd=CR1S1$n%>4Nt&3Wb;RR=%H=nm5ik_+$n-t|Pah z-JMl)Aj=lnuzqHJ=Oxs@x|BOjh}bHT@&1lz1#{Vl-bC^`Js=SsC8rC^$jV4+?Zm4N^0wnQf%OyrQt(4Gv{aXP?{ z$C6(zz9giy<6~upk5hf341s9gI^}GHUr{}eDNi}+6g6?y8fjQRonJLi(LP0(Vv(8K z{5G$1Yft?W^BIHQ?2nbAIN91LEVKPT;`fA$wOwHHB(?BJ1iJvMEw|wv@!C8Ha})o+ zLN_j^@jXc|44B&g$|e!mc9|LIvtNNm@+-e9yQ@=1@%Qtx9?Z~N_?(GD)+A~vhQKcl zXQ$_MAPth^KG%k@6~ZZw{jkl4z@2KvvH1Sf6ajw z5~0>|yR5T8_NxgsrWQTGs1m!B6Y*Q?2{ADF6`zt=JhUxmUfi8b|D?bkL5tqig|@#* zO8yYZHj-%@buA0H6sK5lnG5#4uldJz*Q}tS@-LwLe)ayzf^J&p?VY=N@FJw0wUalf zn)^9oNz`Zc9WEJKS~tQsAHbzwsVUA{Qj6IvzelZpfZDzUj>RaE+3mZrwyN3e?&%(L zo+TxM0iDf81D`b}7{aPT@LsWm z|LQiIjn4tP82q#KL1U-pbJDRI$*;l*cnv*V4k?yv6_?>$A&mjwJDC0>XC z_=ne2saXyg^7BAYPDPd}PhuxUnoxt@7^Ov5nKD;LmQP_k25DgxmJLCBJ3kq_k#48? z0QA%bU9!??Idu1hR;&Qw?LI6vKF4Z4J_Q=zymLh^tqQwhyE=_1ybfh|Hr3U?Ov$P# zR>U*5x^vqn-=>r5`Ku_NGFDX2i|hPd#4zO5@h=l6wtt5uL&wjvsG~{R-Kq7l)r3Uy zLiqt|gnw92*im7Z8&P+&%BD@EQ{BA`L``22B{+u7bRg2?TJ;Lwt?2L%qB5W6V!paN z<~iXn_Nnib<5o{dAlj~}$Dmb4+pWp$z$&&d{2v`M+{JyE?7Z&s@^JUx-TC69AwtF) zIJ9d#?c%3+(dN0FHYPT_4c9Z7X6*cia{y>4GC`i%5gsp_tsf0-uTK-c#h_Xr&y-Ev!p3^ErOgjYp3Vak1Dmv z=wY&aRp^7+il3rjyz?~GVRn(Wdu=D1HB1HE7(~~;1R!=4ttY7#Fxg2M%R@M*wi?>Z zlpoghZ=@#<>t^}Ws2W~V^yJI>Adp^p}z!` zu68^gd7eKlAAf#6tb6u`mwiFHbBh?2DBBYDYt@-21GAP}9!7Z65BptQ+^2@f$RGA1 z%ac#5%YidE`*t15{=xQ}VpZ8L+4xT+iZ+M%+pjHGBWKU7=nJcR;czRZjos@a zCzn8Y;Q3VUD2@NzhtR(c=Rtw$?>3i@S-vE508{;?;tv*CPTum0&B`vMyM4Wwk925l zFsz5F3$#Wk6T40ZNbnFFkrkd5=DeP6#0>51iO_g3Ek1o~nN&ZG>N78t!t$dor=?dU zi~!hjq4MTb{}Xm`*?cgJ;@o|hbCh391Des>zTM!5E7>5IcuJ5>EwR;v=+d;!`t?d9xzF z-Xh+e#?tU^FO#Rq4d=EH3H1f*J0pyD$Gt_6GK1l)*Bp0N|D%v1$Lx5UOn?j5{cs;2 z-ooe5PLlWaoy|(-aSmYRH$47<)E4O8CO5r9nZftX!5e>f4ruPG6yK?ckd$8tTax@< zHb^FzOFR?8oC&&o>t8jI88%#N7_?nRr8rhbltEP-HRjIDG>i}Arb1@4rwpSZKC=XInU7Nn5-Sj>t`gjo$1(?sAfu;;q^rq)-6_B+qcGoE=d}UMxM-LKYn^$P0!3=r$P&g zkxQ$&xcPWWJTxW1lZ}CQ`Z4RZwGq%9L|~wBc3zX?F32Y)=Z(Zed6m$5D;leVP}0RXCr8WBb4g;bJtp&5C%7?ed&2 z)WW!<2O55gY;k^*Z6&8&*-17_gdQ||^&oPRuO6@wO}lCow3@vloA0P@nD+&v*+FKt zEHI#&{7hm@RAS~3eSKusU9pQ&go5b+OHJQL;|-O(bQ5gd`OWTcY*UXnA7}^83;`F! zay9g)-_sJknmDx3H5*{nn^D2#j@8)_%Axjkp_pz|%#!K8kDcW&(i)5sWu|=?|D&no zA@@wuC6Ckwc*opZC?k_u5(A>9;RZXZgdCd#4Cnn<89gLcCGJ6FQ9kt(;A+tLl(eNU$ zUoKFr{yD2(OiD5M{qhc7j-My<8nMgc6pQbw#k&GC66TP=0-O#w+Mn>^k}Ay|pr zy`)h)O4Un;yj0weQNh2iKJdpjzkPR(dL7Vpf~mgz7;sKt4CLg zwfZZUmZ-IEZ+AC>o%8urhx^6rvs%Pj{14=#PRT|XF3ESeF-#x>C zNg~b4=^(K}x8ZbMTDPr^gJWH-91HB~%cqNx0@jhMeV;8$v*@~dN)2s%99;?CeoF3w zZN;XGT;vMDCq&9bn(h`mmR~(*WpytvK5QKy9erAk$_-?#e!RJpPK}@xacMm03h5v! z`Y!03)tdU2R8MYz)0b*$Br;9>nh}Ng18|PFnBAS7T3tFe|CXqpLdF``qd2ot<=s;O zf%naK17vCYPTpjsp|X4L)ZT7s-hrw>uRYMx>5@0C)eA1eaaP1@I`wjpmc)I>#o#NB z>RR8L;BzA7B26q$2A$Vzwm~uH(Xq>k`4*F2rZOvcjom`tMdPuy2bJ7g%iw~+(<`C2 z8aJ~a=cC`nU5MEq{7FKW^&2{X_J(iEE()V?dz0xM=q>sCr7$5ccu~{8x=&G1$6dck z-IJsxtD_D%z$y$&5Ip4oa${4MidveI7mU*P~FDkIN_Ve2CeCs zQNOs+=9?Kso`OZ%xz7zXP7o_;%mGnBoACsSpe@i27C;C*700-YXnn)bB{X&wn#Vst zjmRy2ni)0pW{j-cmyOWEy0z-?H?#`8lh!MUEPA?!zZt28UqAMJPsleg!yJotVZi3F zu7=(_^QC8q_!SSl64(QJX(j`cj<#)+gDy&4fMmX2Ixt8pWhBvJj2=rFuKIRqfhHIo>_-?kRz~H%Ah=1rDLC9jC1_@$ z!6NFx)R`!0$f^;DiPvSSdtpY@7SP56t{Y>dg0>*VJQHM&W*VZhPKN#?<5cyDH?3>^ zS^U+&Bh~#q>qq?firNfSstI2gv@3rcc0s?U;AECZkY9?d-5zsgU#4FRV<6z(7yI`} zkM$_?U{2EQJ*exlHo0^6Z0Eq}4qhq#4XzycQG(%YD(uR^*Z*pK6rn{@fV{F5Qx#c6!rK>SxJ zG~YgPhMRINFsusv8Bq2W&HS$VWFQuhfk(AhOSbfl%KHT^R_@JsM6ji%pd;}a{hmZD z@!CA4;J&=qC26E4{$VpVmCANNhv+09(+BD-;2_okj`eYqYm|C#oYlU=v(mA-?kpBF z;(*Bj|0(uyQ@i%KL~QFBv>86T+7h(2WqH0{zkJ+xSH>k#8jN(p|G2P*vPYw@tGjrk zDcJ^4Xk@a0#Yf_xWx|*!fuBJ;j(&C8^~le_j>9cM5zbvX8rwn>q)+^xrEhNDsT(d~ zuC}<@-Q`_h6#WWOJtL+8!1IFL$jLXibZyzP+WyciE=3Pp`z74)O|bZk7<$*7Fa>ck zQMqw*{9;vO?9&hLYG0w+kMc{dM;Q5QX$rjC_&%NVIv1bUA{2dvo7O(DLVVqUk3ZBU z3lE63?XYu5KU2#KPoS`*7!=?2#?@Rp^*EWv^Io|cqF=(5sx8w$g^>TWTRmxqR?e*0GR>D%~HY`JfVL?$J; zxKwCej8@yL?=@#Xo(ES>p69xBz#bO)9eH_S%e#=l0JGRa2MGf&sgOll-!DMG`#i5x z=1!>*RX#KZ!b88oS$!Rhhv5(WFbCpZ{uuiDrpZdUrpIKvpOJ;RNk8xAWS4yqT^7=6 z&^ZI$ZA%x*0}tk%`u_4vpm2kLzf_};kofywb7C-ijwKOxArBWyXzzTNrx)R9!#L|t z7+wtr^x4!1U0?ur`j%+;h3+$}3+`g_k_C%DcQp)&)F>q(n^BOl@T`uvRk{AS`SNT= z>!&2^27>cj-=$7Wn*aa&;yy7a}VTRgo900jiBU{EFV?_t>4RRjir|*mWu1v)NwIeG zx|+Tgz|u*FrCOl^i1@=gUC`au(dt}g~Mo*ZBaXybB($j8`m<>Iud~h@ zrSW7p8}HAhIgAJdt(7z5E#Y-=pAYL%njGr>A(aoH^;!f%rl5!!=6TEaMAjv+cy@Hw ztTc~xbQ{+J(tPF&Oab%AGQlD5_(Le6!ZlxKuK+iWWC&{Vyn;enK$v}h>IJ1C-Oa_BH7&1`9WEYmmkcP~RxlLJ-x1!dI*)MB^m%ckUxETwmC@JPq{ z_F-P&>F?!JpAe#>`m^lwlnL&?E#NKnpJ`NIw zFfdGlMl&B+^IKWa1w8@|-fLH0M4u9!gZM-qvRHNiv;gQPGg`tE==jk{85C$a7*y zJl-KE4_(hD4I?CPS)Vh^n!>^jdB~bzY;C5T5H!P<9|--SUny#K)LNN>;&3u^9-^m; zq10pj$t9jmI8r6=>=()2URB_;HX{S3Qt(mM7_f;$ISu_ z5M5Ay%v%kd#EK5(G4p)NaRUdAA!;!=C1C1v!qf0*1ez(9En z2{UQxe~^=T8CV!uWS8vKf>PPEl@P%=6uKx(*e}@nWyxL;g<)F+&M9x+SgZ-TL zO_BU$LmiWRF%aGKI1;oZnsKwa+9BwSluV=?A-YK?0UdYE)l!*Nc4)9PVwXF@29=!S z|A^t9fQGX+6@+h=1_(8UyuS{jWJny!Yynb*zz0?h-=YnsyEY|sZIFI??|{Xf*J#`K zz+)6jkze#?vO)lL)A`&wLGs&$xmA61Bzk_KAE?za!4f@d$4~JIFF@6KZk@op=-kE| z_V6=7a$HpCYe8iLjj%hil^J(R2#UP37tV;Hvtj~<7ali`v9LS4w!gJ$He?`tBTp>v zPC;+7#k^lt@D0{?ATdE=Rtiz+Hk842_&pb%_g}+3leR0XLwd<3!PpvDA2nZ&DN&fw zqDfd>Ft8iiy-^oPe~~WhtG)+wT_c_8o`nv2&Lm4Ot~KU6R5~FHOa|Xgi99~Q zqxf#o(k>@QKrDe8bylxB!1BQtR#id!%q7$z@gon)vrBRO*Mb9)yV^>-1V3D7Rd6^4 zFW(qtm*7dv(6d+WTN!%qd8}RIio_Jo?|x**y+uiICmy~T60RQoC8LS-M*U02vF%m6 zi}7{3_*P@V@3osvIYWX(B`qAoD`m*vR-Oi=oG|gJ&VMqQqni0<~HHDtMVE;3z zFz+H4*t3h#nq_%3#R$t)H@S98YKY;}-_OthPkNXZCU{8Uis}Ada1dYgiRc zwwoDc=}wOVIm-vPJ6HTUEQMw_K_b$87}iC}qrmL~oejEhGC)vsH_U@6yRY7a7*X8z ze%FNJV{5CWr-?IH%#?j@G$a#4j+GEo37{r^^ea!~+Zha(+w*`B>me4PD=*ZtG3-h; z4+ixk`J=>8J2xJPHYILG+dPMPs1u^|2b70anS#Z*iQ1sKKf$qGvsO_@@f5NcMoU;j zcRl5YZV;Pi6w?~Cg!pQVX+D8cbH1_^5)T72XG|2{wSGgv2X1x`j8C92w%7U}t$6+2 zV5y5zHTZqCWiCKh-cb?!ad~2z-LB`*eBTj?(N4lqjtdu-!%bZg&arBwlRWAyq%+x+ zqH#Q}U5!N_b?@y5sri^5+tIIiub01&$>jP0St8Sdqy&LfOJc8^FJe~5ZH$aXDRcBemdkGC;R1|$Dy{&4eJ7b#*rp9Mt+yFgY59VuFk|2 ztY6t7l|IHiX$=>kGJL&>7>R-f`dv#lvm7R>x8SrMGCkjb756nCQ*r8j0)N8-G4;>8 z1>hdx`88?@=QN-(dG!z9XjLhbFV7pk#?J$t-jI1jmMlXb@yiY(V!y+`ENb9HJ1X8l zy%OTEj$i!0qXxDZF~zbD`*f<#CUd|-2I=SgQu7_8S+75T_30S^e|(Ky@-`U%Yq_Mw z`^fsjzw_eEK*KJUf$+dwZt(^?BlDm05mZm>xmzFR4RyJk0X!=d?eWpK>&8epuaJs-g zo+o^bLWG_DA()6zeAyqYfa}5>Or9XZZk&Wo4V07t!JIxEyD}6}M)B>y*H66dp#tOX&qDw9PYn5=J5(5(Jl< zugGo03f_=TL2$V0gB3==ub=p6%n)YT3=!h0QIfhx`moTX?m4hxH?YJu@jc-=A#_8f zua1{A$<;7b9#m~#ou%LCYP^NMp&a{hYH6P4_<^$tq;1`|!bs}7RTPhh4c%$TFXmVd z_9!@af&Z^S`hISxxRKsHe6^VLI3`tRofAH@93vhYqQ$m$+g2yK`JJVS$0vC5wc+?7 z{Ix}R-0Wr~X!eHE!R_fs3L#n|E%W_EUO@6YGLD|6ep5-^JDyq2@0WqKk*;#~VX=Yx zllSJ>)@h04qej!82D;3f;_2B$qTi&#P@CwgiypsNY!ZcA5J&lL{Dj;f(^2c|Lv{*o zw)=nVCFpWmJievI+nqan>#E)O@Sai531eXstjz&v#`1E)LjwAHhgri1s`vR|J;v3+ zB1UwOl~A`aM!MD_o$W1RefJ0(IV}ufA36WIuY92?7Y*d^AJWpL2ZyC6#&$!B#X0ek zXMiZ)h=QjOK*uOPhQD}53{M0)S{4$RE43=q^W0ftTk4*A{;}GAI@Gfolk?%(EYIEp|dJ zogk~B;zVPH)l4jke+pPpiS69wxX;iZBB!kTaA`P8bFk$zSk%{D zJ@DFM2(+mw_;q6CA8>o!pqaGZ+q9pQuQvra5Q3%Q88;4(Y3!$c`v_RC93UoVJJc8$ z3(WT}B=kcxU%?j7DKlvLJ0}a`PxWaNfT!U%ALk8zb3u3H_#kvo=IN_4k1>m<=w$av zpx+0Ow-^hOz*sqMh2@DUYHBGi_FQAn{rxr^Aj*k_iJ>z2I%PZ?HPp<6zlq~a<{8ZY z8(H_h6b=dih@Yn_VSj1SeELulkn-a^0yV*6CIrvMG<@=qK}S-W`WF>{4t-6_R+1fb z`ukT`C+pC@@oAPL)_KpR!1{kdO>jKK||+ z;I0dLV*zFEdjO3_s{r5c7i8;;U1p3Sz?gmYM_bf> ztj|vTZX1E2Gvj&wMNgfHOzg((<~d!Ci0JW_6F1}6j_V?`IBxc@nT9xfP$Bi_6S9NePOB_etDC^z{7a%IenIqL4#Dh!MQ@#xNT z7mEvA#~z!8lces*h>=K1G^Z_N%RO5d%I%xktH|u|-Z3hZ4yU;ufh)N?$nJu30nN!R zTVARTr4OxOW|mINNInsR$;S^OfN;;4FoTOO&b_$s;>J%O@`eXnIP!(^19}#`@p0+T?TXl1!k_A;v zO7(0y{d_fpzLvjIrvyXQpKkAbEO35}g9B9c4WpB9jZpuh7vE#zo*%mG77nY=j}GBr@Hqimw? z0Q$ayE~4&KNm7@Do|6gXwmryeVab?1Kj9#CQS}EyZ~%f3-Sg{|L=0(dBa(zSMB_2R zk}%ix{0AOJ497ZzB)P8*BbtI=v8zKV1Wpp06`z zEm;lwoNui4Slw}dm(&-7hVklf-{AyM{)}kJreUFAWK~g!>@gF02dHV{pn(#om@TP# zUuiA%1@#(Fb-Q|0rq2tDf^nyXgyqRAmH9a23_M4Z(?Q+ZkjPTtfEceWn9uat~5R_ z;X+&KK}jM=^7%JME(;PbGtQeN=VZuEzdRyaGJ>rdrKSI6aRaVc6WG_BS+36+4+x@3 z562M!8D<7=fQA;F<7oCCvcuh;rdf+^3i6cHwHJP$SAVJ$-et<-&m$kP)LQ)PjPsnv z;NHVUDTSo463Q4(-ys+T@U0s;6&c&<2h1M3k)}TC|3v2k%bEar*F7DRfUUu{?dv?J z7VRf2Pwv`kB2S#dRz1L~L^an}@19=4g&vfhfvA?x{%8AQ#m)7c&Cf~8V!uMRNa^^; zbFwZF_0h8)jrFTnS0eRZS~7J4hkIynNfu$n`Jp@qLis9qp6a{ZF!nYzlQg}m9ws%e zq5jO`V+6g`$SCpH?kqBj*WF;NE&dFA9oh<$4f!lx{na<2KS;jIzc8_jeu@dgH+TIe zsM1kxg)65P%zh9g9Z8<{@AZE+YlB7|UCw%i2Nw`gdwB1Y;rfZ;V45cj0ndJ74iXF$ zSK{tA0-m}at1L0>z}!J77bAJD>A_{D05y9smYx#TkQ9;kYR55)7^A655@EEx@kByB z@qn(qasq74QDVOH^K*Cr4Ydop;GwX~m;lST=@19YH{v0>-zb@H=uo=yo@r$U@mnJ) ze(&_nkA#O~%95^$*l0vVE_pmPgKFN208)%+%{Rzjp{ls5>K#(MWe_~jB*2w#@d2l$ zco5xgSBs@Cm}+{~{V~(iRrv1~F~>Ff>oL5963F;Rorj$8!xT5@W`s$H3cag}sUUKE z2Rdn%#Frk6M9S4Dy`SiDNvfDXoGm6+k<&dLanTr3QZllKY8x3iJ28)_c!$0#QJButvPlRc#ufn%C z{618$il0%m>w~Ux>c@}n8t299uE#0toQz#33@YvfPjQx463{lzKXP(STz2MC?~1)} ze+Z&|oe@N~MdO$vO_F$;)VkdPFN`dTuzpd-kEn0%Sw?nOF1&&1eX^`WGB9jK%+Po6*w2kjeQZWpj4fJL zvM*(NBIk6`Jgh_La~d`zhlhV3Mt(HUuN__d>7R==7q}@8N1ag#ty%FKRrvcCxj8wF z&;SBQ-574mONaDJtd}OSADdw$&#|K468_bv)^21QFA!LlDnwTOLOYDM%P?5bhhx&c zHVNe)nBOGZqN8oxy);>J@X4cb4Z9TDAMsy#_2>2$TZBw0w35rI5A&|>1M7!v_BJ;@ zcIH)csbpM(7WSwLx3SqR^Fy?1R0J*%b4(EP(_S5JKvQ=@qF3Ijiya0EvD>~Q9Z{@s z&?_jFFy8LX_-bJ!>3xow3!U;qi`o14(VdWg;$#Xxgb{d1hLCsN1TK0}aM>-LVS&$A zdM__YnKuO{jzVP4V+bkKH;MaRHt}<52kdg>Xo%B`x-y%D$Vz4Va7Nantw^2`{~&XI z=odPoG7%mwgaqeSKAw7;=Ghe5MTX_+6pGlp_j0E#{*-+h0A0rvqw}%R=!lRp#jY6c zmpVxNs1ee}w>3AR%&E4NiLQIh0KSTe*U9+mrIHXnb;#0&ko$V9qvf&Pw4=t=Y%|EM zTI0Ubl^_Q4utOz9LH>;GFg|U?o@a1xPVeHXhs#3mO0JhR=3a^wt|l#oGY8cs>28Ec z@EVg9CFQQvN+a-pNVSU_ek}XZn^4&Ag1qTVjA+S~wBstXz>0{MD+#F&k8pryl8SUM zm>66>px#8E>p$hb^jlkyy$XhFnJWUkg*)<0zL$UYDgm}e^WR!{g7_RCbj>_65ZagG z<&x6K`VoUpJW*f}tn%Ua_?q3iq2itIAaqfcX>J&V5*~IRN$Jv@n_lIAFa!Db)BVb7 zPW7e7?HagnlNmXpBdq6&309RS54pr73)x@`-Q+kw36q+58kR1(kscRBS#D=ulRvW! z6S*diN=G6JCer^mhtFTE)p9NM1}S1@yE`Z3rzkEB`ue18eG@QLT)tuI)KF5Ryk|x~ ztel;=p+7O=vcXuFOwJQmX(g1B#MiF}pyXgSlSy2$e%l^f*Nwwtk6HbYoolM#YMDaI zr%l(9=swS;7Uum8i7{v+g3PX*8($uM>xRteh;66ooeF9pE-(}ZA!wv~m7AMF6R)St zj_6RN>ERd|kCOX#*`eczAj2-vEtLq&oD_ZTVcRD^dZ4V3_wIlnW!nu{U65i&cCbK; zaD{kwVJO#koOAWTzw>F+E0ER?9W0a{u!PyEFgsQnkx4P0;7$+?Q-HVCG2^Edkd{W8 zxPQX%)5BP}rRFTA&WeT{h#|Ejs?1q26Aj=MoN4qI}p>=O8On0X4BSdVud282^07ERLlnh+~e2#GBpU;IUb0IXMJYJ)6i>U5|P^!V7P*5H*YS zgBtP{{HHz~{=e#DKab>}`WOm4rfd?zdKu9W?{Mqak*WRlua$u;l{5FHZVzb+E-i)g z%M3AM_eecN=wV_!=yfqRv0bO2y@U$k$b!=3HIw_BO5Mzx^#}($Fqe~`2Ove3MZS-g z(v0ToBN<2Cf+YNBpFx1RORjtC4M()3RpE_Z^iqxFbxU4L%I5H&BJ0PMoIeg1G*Tny z7R2~`LY-t9(edHl-^8WfyrUxJ+o$;`mf_0ub_EPDi*P`BMtYD5-dpVwPCD!Mei2i* zh5*!j+~-dKFTtyr%KUDTME zGPhhsGYikVsm0RqpSY``#r7{iD7CPDb9`EjhsT5C%=}jD;VM+q;D@~2>*dV8-WHy2 z*rFQ$D4`l_ilRbG{}y=ZL3`IMTc^;fIC?bSpD$k2eCe(qZ}!0*+?m|vHbWLVZ>ffd zwc-1*S}G`^7jydihd$-^JI+=n;jIlVZwzqvBWD@h^yUu~7N{rkN~Bu54Bnpm=44d0}s-zJ`^V>`6J@|N5^sxL^d z?Q=)+{NZ{v#LAeF#sO_-GN6~AIKu|c-cGgh@0yf{jgXHuB|NuLWRbaD(1@QXT&O4@ z%(VE%wA9=p_=-PCy{4jws1yeL{`nd^9tkn5qiB*zGWlFLE_80i|C$xE&p5n4l80}H zf+=V`HE%U|_>VQ_>g>xX%F()9P@h^=DBQH6fAAKk-8h`{{dkY1y5qZ(v+zn;YnK0} zC?iR#XntnW?^K3xYnxm;?g&(FJ#<2Ox&Y5kE`KjyJj(KjsMSA<#eyhsxJxs9>aW__ zB5O7OPHeiA5-R(#*ERC>t>*tbK_NURre^Nz9(rjppKOf(HRPVkLd~+olKRHKi>UL! za~6=)J!cSf?@uVyktMJcIAGb?pa<`Ic2_bDg3SvR^rx;9Q=Jl2%$&Lq82`cTUQxG* z;1|+PvOFpk+PPtKKEE*5i|S?#@+9#IkVh{xDz0bSC=DT@K*&i)%Qg;tTO}i*Aj(Bl zYStVrww5&16GdbCdsxD4&C=;*!tMDq(jypr@MVHWUCql_o7?9}D^6>-!*eHNz2B61 z*XRFNjXW6&rX;Pp@w0f>dMDS%9L*_-#c%=c7D^R&g!v(zOA-ysC)qc(MmI#kLTqI; z!K<;jWpfLAD49hYg(n4l-CIpMl2_F%g~V^Jy-9bjR%_ekw{h?d5l6WwR?%%~Os{}; zrW+EcFAqUG&gXi0hWiu|xn4Ba46B&gAD1o#}4lyww^%86evJ_L!O zm|x?@wl(i<$W6#cP62TkyBY@r7hz)=-I46y6C<3Xmj$Zt73t1CD^hco`O)xu{#ZPX_^3JlWq`hSP_PX%a2g#PKWA~wvcS& z@pa*_HL9QsP;Xn&il)IsDRk^R!gjuAAYV>w6&(#GguBz5Sh_;H1SuA2lRhTxLYH2} zpS3;UU3s~Fp?NJYtS(G;@FGm|ysXrc)94t_WN)KYFa+&Fmhaq^U#nH%yh`RENchCq z`7tg2wuY_6+}UVS`+xB%#H40wgNIDC_5<~E#vJ7@w#JJ$Aw9zlk(H2c)Nf?>Xb1l^f(tvO}~P0JC+|5+tiR+Brc zBcJ1G^Dfm0neV@y^?h--?C>+ocXaa>cI<)s}8ERi42$K0Jr_<=;`H8H-2U9*-3Vn9b7!EUOsthKV?i8DeZTQMRp<@;r z(Jw!CT#Y$`>mFPZi?N?@qH{Cai6hAN!~NoM5K)xagHf_#r*}U0Q#~Vw=AVG=|5uNU z|F2K7%Pu6Joxz!I@A5Xa*6IoORWHMPy=T2ZSxWifC5d*;*F3rN&P1|wAY8Wak5*>f zX@2h(>$xxad)6e@sj7NA`G;}FxA;IZ_C=|(GO53Z$xNMdQM)fW*?-9_JtWR6#aeudS^Z@*CR z;EBM<)=0#+;4tQksIyb=wBN31B$KBl3afGLDl#k8sy!n8qAODA$*uIfqh(rQKF}rq zWA(R!|FukVq0JoW2hRaD{OPdaZhg3oI5jDz&ngJe1P1s*+F26Y9+caR1cxzhNBn3n^4iB)aL6qG$ z=uDUefrYAeA#C&L@1h^~T4?QShiyuFMsB{ky=(f!b2Z5VE~={yj|Fn1v};pYr29<` zYMfiT1p&qAr0(F@hg*cbGd%4F|H`I|Hyoc%r$pu#N(Mzw4Etsq5IpuNEj+|FcC*yR74hS9>yys1d^S z;}eA~fPr5`Dl%Nwy>F!`7Fp=uu7UOB-C z>k_g_idX*mkkO2NW>gzxBj579XO1f9XgEvqgRfx>Rp5{!!;UqB?VfvAsGB)Gec9FH zV{DhLcoXkz2C@tPSG!4RC2zQ5JHwgv0OnqCtLZEL38@p^ygdN>qNlvkTXG0|6_Lp2 zbqxl};XN!+iYSV>jck+7Q96jL7MezyDTa>6Thx)~vPF{bCF0vAiAL?98G z(RUpQ$QHtjDKJXf?`Oy;_t@(XlXEn2G^U;PO;FAGm37~@UCQHV8?|R z4bDwXc@?;S@9CNDVsACaPrVs;+K(+qwk zqz|c%8%6{Cs)j5S=K|oH>jKpV0e{hUSdr_{IMCYFwvCAi;1%Py4&-K6_yxRGT-fJ! znu`^Ju3W$CP$(+*GAHpBIOEoJd6lU*F_F&{HSdH5 zUiYlqMjGC9uoVs9;_9p zud4gr$qcVA4SO#+yE)7i{>1MO zy~9agNKGBRgUNb4cqUfd`~9{n9NGcSFB_=`<4Iph#j}R61^!bC&lqg|`Vb5YHTM~V zTP^Mp66bxc@gt(qGNoOBX08qmIkz80L#~!3p`OY#FS+9#VyyXDqC967Xon>PPM=S9 zrLqDta7;&jI3LGefOuT!fDf@_gXEZWM>QVMQli zF1l`g0Q#WO zrQj|78K3k-ixK8w_4ZJFF9My^m5Y3(pK9@O$!)PlqD`zwPE|7_@Q*qzv0ers-_ zgY11^$*a+?dWBEGn>iL=omqHAx(>CTtU&o?Q3t4H`2{RE2|~P<9a%wFB=_xlMs$}q zES*&JO_5wBf5U5kB&Av>N!bdN2@t2|s)-GTi~pr)43PybecBW1I^;hlc$;%lCaiH) zcqzh?{MEahuGD>Ia|cDKaQ+*8Va<|j9pJ(bLp0SMw-XEfwd@_qeIX_g`%5=jCdutY zB^?;ne)aXveb0i#B5K%?v|M`65% zwd0s_top2by>>@E_5qto3Zn^LE)3P?Pd5Iuz?a3|_Ix6zYxo;}@N_VOnC#6Qs4qSa zc`Sgr_glLy&iB3pp{7Bq!~l@9EABh$qizl1(uHsm!V!@S5?ME38g$Hx6A|f`))1fm z4}_lcY^U4M6z2IwmZUY5bTb`W|F2-zye*bjSg>oyYL7POTKU!&>L0^{7^I-L{MOHR z#Z@QZmY3Bn*mTY3*Y_gZC7e_JxVT3G-95v*1(ph}^ASrv@9Geyv1)w1ketbnP83v# zATPhDv-FZr8Cly`-oTwN=siWw|6=kT-I1xahCH8<+XJ~@3X_`;R6Xvj5f;AjT0=Z6 ztX%K*OZSJBHNTdx<3}edso5&rqIhUL7?V_=_9TrcjwQX+v2DWfTS1rV$x-GL+U@6x zGkWO~>2|yWJl_Q-tYa`#EGYBa+Auy2+kgm)@kSD`l|1UXUqvV)n&&uzh{9mCvhAyp@mF6+ODO`QwVz zN1%&Low4=`asw5_Oq%f2drJipk*YAb?pzp#Zl8T5k``#1_DjT_C^DKjTn_ASFnzal zCLCY$v!Nq`yeTyrZHT66y;N8U7O_8%N~W`s*u}GH-ccV14@qbi^0~%aFw}{Rws_P$ z5ox^S#?n@-QLG$1#tDi0C&6nDd%Kmati=6|;7A?897t|%oA&Y|#z--3R}V z84%RlRPL>%lAy^CalZmyy-|b*^IkE*#J>o0iZ}9WE${Mf0peU&wzX_6e5^5FG zjs0Guhi;=QHTGmv|06x!Z4+H{6wYMyr85-iT2Hre?5(4QG?+#VXR^9`Uq(mw5>z;f zoe~EnQ)xYA;l8O|N;vb_jGTb(XzmuBXNP{FHiCHV-GRb>bAFmr0|t0}r->E3F&&v-aylx$Qu>Ydy|HPI^JD19e0I6e zF&09qPmO(IEAuK9Wi(!@KQ=VsnJd=bQGXK+L(hW1>&k4VmzPM=bsZ1d4~}YLMLg;B zaSDBDH=ks3WC$WM#PhG;))H4SJ7)uSAoqQ3!tI4gup8%x=#lk9UET^1$#%DqCDG=t zkLz>~u`slYRjA!j$Fk&WP8k_`zZ>RoH5j3siSI?ihoZJ5x$a^%^EpMx1Bm}%vv8}f zeir^_caQaIyYAlGmqcOQtLD##n9NYv#^gM0MZqR%X#Fx4PM>zk8jY(Qmm^4^OkG(nHAWqMx|Tk>(Jr5neb!XFsrPTB2w#;kGNg z|5l+_kh=>gQ2h1+I-F_cr(K8vwD~z{qHd}e*k&jOFDBiVH{ zam`C#Q@Xon!LTc(BmCl>^N#n-P{$GfaEre;`d)`tEny)|ok&$AJ6HyIa<`GDEazg` zO}V`JC_1XNOHvwKCkkHGzlVHtKkEZtq>jU6pT@3ku$e{VsGAC}qR?J>YpB25>duwt z>A3`|!e{CN%gLAxB6WWx81zvb=5fd0fur2@&v$|8o5sXDcK>AFlCRdl+aZ?kVigf3 zH-DVRq1lf9rLhC)rm;tXm(I(3XOMw5wYWMQ#G(*G8cI=8WK-l*!MJEHB-&_`W^bgY zR}$&x%OQU|qpGbyA5n#PD`x@O$sokgjY?NvFGd3$8lFM$Z_6*Z+B7|BAJIYv)f;x_C1ZC#nU(6Fzzu}v`%hTDJCua`g?p*&>%4G?GX z0Bb~i=iLl+L|yDiXv{q}V(wzOsng<44^!t1Iy>Vx_xsb~Wp3-=Mf`>Fw(D6e%W8rj zUTBW)UAE$O2BjIr`M;1;8}K0XCYY=B;WuiTN52t$%Uz_I#dyr;_3iGv@PO+KqrA)Z zH^KyXb&}?zknl)e=oJpOM2q3GhkTh8IU=%jXU@%#K!A#9+7OzlPhRm7b>2Y!i z{6)u&%*XJ%L?#hq-N=fk->eqP$~nH*eot0elCSHW@MOFBjy-D2oNn7+JclvX6`%~d zG~Yb`P{6WuKsV=vpzQc}i=hwrEQ94&qdIbZ`q}SbeYULzaxd+mfJMVQBVV0B+xni1 z+$xOLX{W)U!^MiZspBJ*i*{RPoJ`ekva@mkoLqT7R)dqv8pRU!%Hk{c@zCJg5}iGu zz+W%4$GE{4iwm*MqMN|9Rd+zGJ>@qRUD&m%5nN$@1PTQ93xQyd>Q|FgL1Qb?z)%bZmsf#-{qzNxnCoz9NZm} zWgRT9x1|};7!B}dBMA^j^vnW!c(d67;=&xHwR@NKJZ+m6aTPA`lHqdN!E)h7hZe2z26fMS<3(8Ll` zR9l5`Z*>3CJgBvP8GQ&w@8DprUyHv3R3F!+Np~?S)f)^aD1UN5)~!V>Tu#Tc#98o& zUpmfb0FEFhSvIu$a%ci?x`(d#@m)FAgpkei34rErh8A~OaX|FMjlLDhaL@(48|6=a zi%3WM8)GK+3WDJ1c*J`sNf)<@i`1iCApTQwv-9@o7r_yhiR3x}k$${tkKTx>g&>#u zvDc<3nQd`b6h-DUGWNE!`$q}|%Jn`?Hoi^B zuB$wFdWTkUa9xNB-XEp_O4{h zY0g5iHSJeoFM*dxp9{Llrw1$4;!X07IpWD7M1#2;)WZd_Y;OdLhaz6(8ht15N_=nj z77og^*Oyok4Eco4iMF7O9wOMMAj2Qs0r#3=hZ1v5h)d3J-SC{gcG39!8S#_)Qv6HX z)V#<*&ZC*7wi7a;x3@1DwfHI>%FE%5>LzRcH#H@KX1bfchOl8y{`|S`0Q;3e&og zHP0m%|5cJm{+?TNPkBPjS=DSNC?-#MXjiRw&_Z=eks4loEm_6#x1ipfEh^e_6KlGV z%*Khdn4)C=3=gK6@f?^p6V=6#UYZ{1ly>#aZ_UgfGOI>pLj%GYQ1LN%(rzak_R zE(ihpAc8t&QN!Enj@KIBtpxhq|Exz~XduXkb%dZDxTdF;>#Gp>jGy{>Ki1LMu`91U z*51m&%u2vMo|LmTy$3u{bNLXF9?zLemz6Plew9nmq9PH1 z=2Zi!%)2UeL=7})x2wYXDtkokO~f+|1~&9(4DbKXE3%lI>@24?4n20FW}XVBBhwaR~$JtwX&#o&$KO)Gq$rCD`f z{aCKc+IhaU8YfWxrliciQo7>mJ&O;`(7D9AxU@9Hjod!!XWiX<_Nd+9d6KpV0C= zRL}|98^7CdFux64YgiQ%-KZ;fIUM&%ZddSe>-GuBI;W@~sJ4Mj@c)GcrXG4&^IAHq+>7yX=eZKy#^W!~164epM zn-*mpw+>C(g~-+dEe!*14iO_P~0_m{^6 z{-+!PRwfF#r^bgVD2s$G?)gO>8RxFk~N(_W0<1A6yLI4R~=-- z+DCkMhHL~%l5=CtcH>tU7r{l$s0#Q)IKOml)RZ%O{@tZQl}H9*j`hUm zP9gy>Y3kU%pZ*M@kBM-ayZNuj2zNfFY*+d;vfOptb_OtqCy&@Mv+n8*(QqGo@}+72 zBJtSBh1blohECSc@w@?-Syt{fGAWN}o;X%QOc_=5R_t7sdTy~}p2;i{l+`0{hf4O< zLsoZkUMR!r^IC^GvDRJJ1_S-v4w3^2pik%3uArle0`cjLf8HKmv<@O&^^cAZic@3} z;&;0{xg?*ukHO~2c7;2_2-6V=*W5tOLqf+IZ=JE-Q8!f0}qva{jEgg|Jk#1R4dH zt_I*oSpD#S0HEEG>*EabN?kPHZ~aHbh92rBokf<&W9TNk;kp)<-)hq;REFE!s<_@l zt$k^Qw%jXlk;5ZaM#PH$1W^+u%(|y|>?^iy%8hYt|Ibx$mv|Nd`*}MaZG844(fyk; zky7%Gna4s)X2EA_>X$T8#IUHGqEePz*HjNhgY+Lm>BOUYKQhT|zaOuh{wOq;n>3DV zaG*8GtWop$`%L==iztFZ&boW8Y9Ozr9A;uNN17~g1hWvYq2h#1tXVa+1fV8Xwq;*V z5ez_;=d#6E>8*fdzG|h8S;ert#pmmT*jn%iIC4XTf-GT^5c@U_?EwlP$kd|wrHrW0|hFY{Yyu0D}?fzoz zTfCP)FWe1aAKSflq=XV$y`ec?p9Y1a)1oLrb7vJLc^wB}`ZC>s88uAdK>WJqKu;u7 z#DWhj5SSB}vGs9`5t!$)K;bz^^C3R>LiobP1c+q<|xCYhth{uCMd2aC9x(!J4YH`_VWAryqf1`@3soHyy+{O^9*s_F_m zI_(v=XRQ;lxdLSUMX~Qxh;=B!R8W)&Jxhn{&-0u*fAbLBJSAD5P7ht5TBkmb|1p2L z<+-7n-pG3MT+@^L-GX}957IWfJ8Jw!%EJg^KN2H?tu1`~OlTvI$(>!4_}!M@5IWoA zB)%1AB$N9lQTlB}YWd-jab0BVC+T!#Tu?@gh)vKjO4XM-DW2Ede_TxL z(FT9Rlci5v1cqwG$ba??Z1Lv|w4e~A31zACk;!ZmlC%g?QG~ojh2Pa12-;q!8L6LR zq-!61`?#O#P2?4R+G-?fkbXufKc`sw?gSi(&p@c9)nl~My8E5A=HhH%o5EKvOu2=l z;3mmM3vEuzM1xyo>D|Sk^r6~#R-l2_NO#n&TpQ1@b>FD+9m6pHgw# zo;j)FhV;vDd_4!|m=b2~`YWur-NvDxT{K<%J6%VkQ^&S%<-m^Q-4A>_&y9g~4`o7X zg2bIk-Mg;Z-L%b$C_?dmN$;o*>8O8KBtlDtuKSOUUbzF`)4xSiXP_a-NQRxkV zr%4bl>Lg@BHnT?Uv+4fXW8@P#t>yvBc+osKJ|#%QyI_sWz0Zyh(5;^bVCZR4q3s^b z$3&kZhlxop-{yXZ$!ElON^vHOZmB{1KKe}E!hO0)+}+rC0|;M=Yr+Yrl-*dFEOrm| za4AC|Qut_7L)*`41U@!Ces3ajbQ`Z*cJVgdVw{+$yo}A{0d#X_P!IvR;HZY)BkjKpu3W& zoSVZdsp$#aYEMLuoGh&}iEbeF$~p3(Vy}APYHhrOp^iD9ZBdDqj8AV^?SjP$9xW2^ zslQL)yeX;a5|5i$BzVAFmQH27Re?~dCG~ca}yHw>@pvvycuQEz2unJ+3H^Cd$FhmzPU2# zsPi@M`{PcY_a8g#>@Tp@5#iKQykF$mbEZg$j_rP)6(HhuVrNd|K8g;kP>2d=E6| zKQc7(`SBZ!KdK)X-`0t%?Yfmq0sFHLoJFOgX9u5n2bfNfw4XF4kFkO;Vv7bd9~iI4 zzMk$49Z-@OP%}%)VXS8SPB96u@=>d7@RysSuepSieae^CBYv&b%!T>wZcvyWSJ>fU zc)lgr8XWCc_2tBQhol`o?THwH-3Fs;HD=mzw7Pfp`Kx|xm?T*f3`xI@{Rsm-y))4{ zYJ>6c&;XKs2?j#vI;ls@5Dz_5`*WTvbo*W#IW^Il-I<6Ig#4jWM@o6)5q8G6wCVYl zi=EwnW83Z8IrRTHw*NiF%+4>F`iZ(64S6CsVCdj6BWImhm&6Y@@brY#mxGG7(Q*UcwLRM7sCDewXG*1Ybo)^{|p=A!sJ#hJX?ZQB09YR1^v9B6+q z{=xoC@dY4Haedc?;)j}{*yFwTGP>JDL`5NmwS)oR9bb5CsMya1r`oc8uLD!qi>{Hl zc-9}NytvEZ8qfb#(7E@zuZ?|813u2a{jVtKvtt#6&busd3GEIGtXuOjIo}ecE9FE< z?O}f|OmeLHjx<%p3*M@k|I_TT`9hBPBlAg@eWwlhW5yeoI!v(m1?b_Ff^lgliL1fc z<{S}rkD5vj1f*j3wXv<4h`Zi6-1CN6(N3$LB04}WM^L~z{MQ%_Pwoz>`22VO!u>Br zKAoZ3ZW^K9vArCbdjL+4K~|c;%g+?N*PjHhvt%+f%9Z9a06S*w_5TyTz8Z!-GFuaO z-Tkwsllyyb1hl65`{QpjhzxQ#f1qrvIB=vFx(&L7j;%WXoACYJ!ab=aI-^~&qUCVT zyki{thU}?!yz7KjF7~IE#VCq$s2trJ1iKJ4qo2=-;{Acm14+vC5161v!ZeMpfDCTE zPTqbIz#gwtp67k(a1Renw;nJ9{y=%$^K>Fqvw3cZ4LE@w zsa%$K3ipY!5t6URZ;ePm>XIDpS#FOwU&K|fhz6cDj8dkG@YU2}ZskN=1AyL|qxWNG{a z?=C-fuEr|}?2Qr+_?^ykb58PSfTFtL9&_AbP`oVm{%W&amQtHx=5vHm1|!M z>6U(<>5_5~@XKpEgx&?L8y-O>elM@iKCoTN7BMDY(SERoPGm*Hm8susONrC!lGnz` zT5$Y9KF+-XWo7;c&8~$j;l_cpb_PyE(!-t*JS~hcbiu-t*kew3)iGPP{o)iz$ z0?u_S^?e_9YYJd%8jnEuM%9J5^P1oxrv6pdmC5B<#2!*DiU7GOQkMfwy{83KJGx$lhg&mO^c5o!>zB4MVPCL?0B`ZR zA3u5#QrSoqmG(7}S65n8o~kVs51Zc(4kImF8N)EiygE zd^qlx4;)b&h|-cH=PU2Mi~7zLtMQ1v3EOsX)r_ORx1X(G1CE}KAbl(Bv_}u*0o!Cm zPYJ#&&eL{JotnWbJHit8jncBX@0ZsK?EhpJg2g(DMszKFZz>Qk<3Z-l3A&#<-&{+u ztx5f~HJMNM^^5c+YVPdsgqxS=MUAI=84~=56_!-EachTymR)s+VRrJ zVq9LATWz*!+gWYE0nS96Q&jLA(g8rc-CvbgGXt}B+W-FMI%tWebpAYZPTaNa%lNO) z>Eb^8iD>`rEYP8&3{1ZeeQP8t!33KqXw`=|`A>AFJkTPR+dx-z3tmBV-V=0sq@;%< zJr7`eg?z`=gNa{K+J7(P0)fZ&R3yeKpI;vlv6;MTC+WyAZ3MKJsgVb(I}=t=`&^<#)jHdP0EhduP9|4wZXbt2>9#n72oC8mj?c zB+aaC@0l3{*UrsNpoe)1>&+DdbCuc>!#`7A7_nMN;NZM@2Vg3)hn1;N4}W}I_KIu4 zHK?2hi;p|!WYgh(I{y~PSwm4`UF({&poD(C{<(_)`hd3cC)xGVWH+R!iuUJny4vi- zxg#;I#xbd6Gw=vQ);V-$)P*sFv3#eoJ`2)s4$=b){>;bLR{GJxQjyKSh zlqIeIbMGW(uac-e@!22V{@LtO^31UssmdoZ3csW}6LmCc5m{Xih8mt9-+5oSrB=-n zQ8aZYwDfVmsoQa5(GO?Q$%$CsT9D+t=y3tz-#;Cir88_w0bUrDD}`_OL_Cixw_<{ ztl0_ zUhpzs4{rKQa6Q2*QTPpM7hrJBhJ-h6E6-HPUUE%$2Yi`lcgYB|_6}Dn0+GsG>;TI9nEN zpN13;I*j#|efmv2jU>3~R-inue3s3x-?6k7m%*r1SX;UC)P(Wz9@n*xIqK<})vay_ z@BmU@RAMe(DUBROn?M!z8q_c8yxY7soB{~i*R6ls!nu#cZ%?l&LQ=5%O=M}aiIh?q zDM=xUu2O}KJ0nej^E)k%-@c&JCqEdPkJQji|E1w=*y$Q;SoAhVmbNiMxngWHqx-jO zXDCI%y1@$r1WSNCFa`4wlMN@;DWIP{Qn-ydWDAgQXbK>>q-#deHEXTQ-omfe+^-|A z5YrIx3$Gp?R~_ELwsdw5Be{&jjU<U=lK-L{EW2wDWz3bLEi|qslGr12|Mjxh}bE;blv3m42j|B_8Rmtw?s6j=$SNQ=q zXMPjy9@pdUw{y5-SBZSVkp`C>njJLGWVDN$&!%d?m#bHrX zd$EFgjUv_`-Y^R++-+ZtbBfo1-{;nVmq)6z8k??(U%2(SBcx9jvn;doCml$Q-t!$^ z*Gw${eJDA?-(7dMa+L1T>m_p81CsSvB8XHxgs(u9+&&N@ zH+Vw7T;Jo^Cyj)uWyIg`&rW}`s{`HSNVHZ* z)(AIQ?te3?2@-fNk@H~BgLA~DS%Mb^ONfun zYcwEAG+?fxR_PMLwznqt>&3aa#_v$ont9{GZG`kem171KE+5wsWfLvkq=Fvydep@C z6ZN5^d~IS_8(BNi6`glFtSi9FOs7z@MzgTUJm9*+>#)Z&N}XF|{sC$^;LfoZ<%}0K zV(Qs4yl4z@FMOk)u32NS{1?uqGDQYVnZ9gez&Rl(r0g@2`&3=~jTP51=yCfy4Pk7*JJgxSGLv36bd;lHa3-`O#2d3x zWilkv_Q)=`v+R!XGX@xZT5vLcc@-vDOFuZ{P~qB1dn8R37q}M|&^}mj>=4tw9}Cxi zSITSafaEGlcZ((xH@@Y^pk2QM=(Cwm7xYlFJg(R2`hOu)m-hMS7Qg(!+cvW~GbVpI zOu_-j?UqSL|6T;opvk=+lXFTy)3Vown^%yRF0fSrR;pbtu`_RiM67*H^howEe6LSL zq&J%Gl~@e64C{67dVyu2{Cvz?gSo8D&URfa|fT2u4HhTSiHBNzRG{5lb;`t#* zg#l@~Nsb}+GbSDJ>0m^w!O4DTWEuPWn-Y-10(MS+(|Dv~Zg!GB-TU~jV%9Fg?kKaE>+!Yi2%MVwr8W1{>5w7)Wu4yIs_Wz+XE zO_Xn9kqcKYw*0%hIafAof?Rlq`*p;>M4TJzO*qrTc$1sy*U_ZC*EM@v8Lx-rxG~Yf zKNPe8lzg%|mc?+2?3W6;)l)6M^IgMY%?^IqKFi;%S@h@&>If7q$0Mk!=^7q*3QC=A zq;W2ARG6qz{?cymcb*9p^SC@8F`Hos(6I2=AiDW>8*y>_-oEq4q-PWC8(p(9$oe8M zDmAcD6tj>i-IoLAA52|54C0P_=Xz?oDhtS3sbF*5UEi0RjH05E|b*t zK^>lp#P#l4Sg_&0cwR4mNUKbV^ZMYmZ3pk)Mv}Q0N7Kbu+|{--tSq0B-c$eJb$m`g zL0sX3Feldgd+_~4o9Z@cAizP*0gyF9CxN<|Sa-kPL=rUck{$$&(yE-Yh1DJ{XgP+|stcbR=5Oot0vpZU1Ld4f4N9HH101jg9U43mK1AnMpQ> z=k8bvTLzril(1isIAe{rzy~nrh%TjF84FH-RNk3Kdn}ms!r$PQ6q63Qdck%0x9(_< z)J|D@MtfykbE7^H=+9bi6%U_-b#>eZvZG6KNY}DP!+899rj;?7`>lzwpzOwi}gD6Wr%pa`5Pq zvdFo^g0hbbtD5hRH4xAo=_$WY<@LAXQ?UUjRuXp4Gerx0HN$iEBeK+xj{dv1f1da*G1esp#|v@fdO35 z4t8yzOJR}zs_TZ#CF$YUbTm~0R9RMi>+0tZg+gfW)yMc&tQKm1wnCqjw|TnjIiHfauq|Yz{cw-HlE?wyR42NCbEoSNH$Ud&_S&ZrmpJlzy8npat z(6+#5CM@V&ZiaePOHKN+?NahgipFouM)joXVsLdvP+~nh&0qxkmiM7_5kXuAX4kLh zY$}1{z!@`^vkD;l7m8K{LHz|mg8~rEyZ+5<=KT5{49DxI5WMmbhh4M%8pW=&;Vo<; zDYD19jpwdW+O`ob_G^TaS`JIKOW!)Wo43im?k_HwH`c$LXL6CNno>L3F0Q4X$Y(2F zqsl#PGKB1cGr%$2gnOI&HGYNLlVh{mW>~jIv_|=6cddtYe$o%>%&Xg%R^CH`*gyIi z-RvuqJDadiDMhy%!Gc$$;QfbsU-^LGTx?*swVBu2Ah99crip#whl4n_}bLAs^KU7Q7L zaL_Dkm5ff}Tia_VH&r6P6{beX zE@5Y8IGq~pirUqzl(&WrF+bI1`Wa60VY$3-*b8SR3!TSkWR%GNjQx&F_nKej4GorI z)>t{IxEa zG{uW}@x&Lv>=?$8+p@vI&hqFoOI}dpm&?S>n5jC_eKC^qdoMFxzbkpKjZO8yZSjab zdhprVc{IC|4f?r`&DeU*`Qq|k!~IpK#;I4*s>$uZFbt6%s7G@3TYuFH04!fW$a~mj zx)TTgCGD=vO$oCl0vl@I(}(G+@y^NRDFf9%I}ON<1bn~IWs1Y52Dve3LzT_Y#e?6} z-uB>m#MW&~su@|vJuiC+cf%~WWEf_BbK!xBeanK~@ zfr>-l`B)mEn6RQ2R?FNi4z-?-9%<7)M86+hUe28a3EUm8WA<%8uDf_^zjMXC13l4` zo2^kf7|Xw~HHphP9|&?3#aaT>vS?KgJG}SOEu6mYY^$O@-9`NVIhLO{m=+zA1C^c0 z5dD-mu$oX5Ddk5ns5^>uzTEg_usvw0&` z`90%e|ErmHeSC`NmMTa0B8SQfO~VT9$C=h08Bvi7w2v-*Rm%sLAp_BYF7%I?)SFWdWOPY*TryOSUeB86PffmY40LC>z2B_Pp79}apDS6V-`o4q5Dt0ec*mFB!ccRaO9j8{)Zq?;cJOi8wV)JYWi_BeF$iJon^`L_6Lkbq&dYJ^NYPHy&{ zrLlk9j*wFiAlIirveD#xExxNb8Zx;!dM8~zN~K0+l0?C+$~n!*PBcoW3$Zd$S~e0D znL7YitCIR4Qg~z^XY=`g*Hx4G@;fn~N?6~`HcFA~BfJSt-1PTuyKy-fgQ8h)Mi8v~ zA0y^T=7H0cJ|Bb&Uh82jH&G}oy4~#C=?nURk7|jBTfJ?6D9ShxT9WG*zeaa8TXm=| zMI&95)mW7F+R6qlxuwpBrYWANKaCmt6;VN`f5R48`n50$z{*BAz&yah0Ai_JINo~` zxKKvfk7r z2f%lH`|SVWdp{{19gwJx2Jhp3gIL*N6eM^#LAYH84JKETMI*N0P8=`E2%k$m(D_gG zPE@zDZ@aNc+XlsV8zT#*g+-gz8Dmd<)MZ;_dVo*5>0heI+2qF4Ru2dze*(l zm%|_7{%w&|O1w_wBB9RufbVIaOgVf#g5~lwEgE$>d+{m@;t4aa zv&-%s;Sub<*owGuRn4@r?VavTl~(^;`pPY32+y1>X-PjF4GBT~*qRfJ z?)p-&6CRm9ap6dN%PYsO!mTM{vgQMCSvFHIxjkNguP`fL&28546vqCH(CYd97; z+MH}CbI^pXV?L4<{ebK)>?JsFeKgi~`(ixz?O!|r!ayILe*x5aJapa%p{L{5wuOp@G-W~AUOM5+ zw+4T%=X6n6-)^M0A)_OTIOqxdvovP0)!XGQ(^vYUu&BgMF*fiduj- z=s7r_Qf*n4C3;+E%(`6pXX=Gq&fV}~W7I~2+XUIq-C6>zN!^zDFhDP$b*IOKX$&w* z=6s3OHzz0(U@EeE3>p>?mHDA+Pr6 z%gL(eV_NEf*u>v2^aOOrg<2cc&4_BE$JDJh9?a->HK7EAcTuGdf1iWz>KzX(tvkKP z=!Ms&Pgc1Wg5B@yW1KOYf*|*kJ`PgTsdd^zzs?J1?g{!CBgcUC^as5}x{56bdlS`C znp+UHR;#4lznWC})qrrG&*MJthm!|)*h$04VSvRegj}`M&@%!2uVx|i#d^u)cbN%| zMa^si^eQTSl2bo7-YU69aeum3WB)kx)Uv00PGb0+bGnB zjor|vzR*b25&EpW{YEnte7A;ei!fJ~2veLs*N4~12a+b3>(2CR**84mLuHTQvZw{e zD)y|--xetnFG5-`SZ}ZQzom31clF^H(rayqH4c6byf!CRG1eXV<>04oLP10|UPSh& z#;ebmjCgYU8k~+2t6oZLsy~`2GM$F_wFi27zru^kH!aMU@~oUFa+)U{U7t4bA>T3n zGww0<4B-0yahK^w8W+sRDbsMZ;u}e-a2>k|FQtGZ7!R| z^a`s3V&PUqV?@ku^7Y%6_yr4Q&&VtFd$x=1IuDmI=8SLD)Q@d0_g7Jft}h&Bb$4s| zn9O(y%rPaH>L)Pfx(lA}dz&~1s?3+`fR3+-NCJwHZpdZ_Y8}s2$hTODMsaRXTu&2VphfqpN_0e${$U{3V56m-#)+| z(p&G}##U+4hA2^Ebg-*Ga8l~+1dCAo?OB9FYR9vz*?31SvqG@RX^)6~V0do*fX3*Z zKI`-m?`0<53ODs#t#|vz$H(QCQ}m%k7X6O-0g)#TI#$FAC4c3E-gmw`Z??A2*LUib zM#ed=V+J6XHG6pw8F2PM#O`fD4p7@W&?Iaam<7WeOG_Uwg&epgx@8nC{)xe=i~Jyj<*7>esDZ)bbYWphSQu?_5!qxG8f}<>$tD znf&}2I4OxG{Zuvq{7}o+e$&{;@yoZ~2=4a-Ld{ey0p@<90%&;{Y3F^sgqINHb`oiS zn^#)rubz_h9ZX&dnHBZR7c3;<9D&g3opZ-WA{ZF#zUsbkV~CdxzYuPH`&d$4CU487 zSfd&rcaZG=VeBp2+EBZ7ZQPxf;I2i2yHl)q1ussaxI=Jvr??g=ZE<&p0L5F}U5WjULW>9$cN;}xbAC=^K7!LBPko(ONNeUGS3jC0VA#BOLRAY0!i)$pQj>9 z%aQoR&n@4nZFFLrV_9Vm!}R;X-7&c)3aWuyR1N%Gy>U*}aQ&r<52G9lJGPbujz*RR znD5Z}KJ=NxQP3a#rQ{0-CLza!8Bc02&gZWlPFDrfA(~08hiz;3x#&isUFWE%7hxBN zbDd899y>AQ7ee0oUH&Y}L8z1;A~X z!r-24VAu>~#1oE^?D=IdJ5J+&`-A4OqU-LCeQwaV!G-Ex zJIUbmR}!1AqJA4hG`*t{G=5Fipw=$j7dZ~u?sZ3iNI8?$?z}LvlW<0%fnHEsqs0Fh9!62n z!syy()aCo(`N-Yz*+n640SU4W@$io=ay<9dRP8_KA^7P|- zj7l_*G$}iSxS?I&&8v4>;L7d0M^D@Vk1vU(h(*d;5fGf2LyzeS@{mH@J`{ktK!tj8i7q zdAZ*+X-y>dSsNJ1-q-b7T#w=;hShU{>7?5wPg0tw*0sl!Zn7Yu%8ko`l3TP1KD2KX zQymbpHWj)~gQ&&mGJF!Ohp`N&Po@Ez-l6>ZL{TFfDE9M{Iy%4ig!@Wg@Md_8+BffO{&4v6j=Od6(jPU0|txEJu zKY=LD6HI-bTzT)Nh_Syzl6oS`g}%J96QMnsnS(oe_QQA_De#Gt-oKL63e41YiModh zoOr-MCPI%X>58z?ImxF@>wqQkKU|~MMn2o;RIpZXP}x6(R$iPPtrnY6;9)V z=?`3IsbxROnQ^%nRXE?d;gBa7@hnio%XWE6-G!T*uImQ$@rkOZTD-@6!riiQ+A`wh zSD~)E!UqM~k3_%9Ha9yb*#Y!pAFtnZpbi=L!1}G%q0RR2o!?}ySw73+uFKYtVx ztH4;qr=(Un(QP7C>l(^pW*%)u+A%irONvnOJ{YyPkVyQtuz17bvC9_UGUAk;*^=hm zCX-j={gsE6W~GVM5b3ic(!*qyl6WGScFoBRkZi*Ke z8TdlUZ6%bf<+AL(T%ww6A11#2M!qvYi0hu=;dxyG5blc?2GDp!^zUf+mtb(TFe!0a z{rnuicxNv1Oj#p^&ebDxlK#*jPx5@%1_?le$>PVNAksa^N_yBp`1Ua}c~4IfgL0LA z&NvBUjB;f=W#C53+!9X-p5Vdk@Bu%Euhq`?|sYN3|%D7Lg1H2G{tFUre7NcmLN(cPUp{AXRGov6D>1C|Qi+4f zxY|>t@nw9$4%v12MX9U(SeGVhrn667$Z|RnZsVipZ8K+hD?*JtR(Q-r+*t*J!8NV} z2xP~*@o{jVo1<27@6cS8n!*vg$!xu$$r#R!gU~(pr%GJ4TT@DT`^?mV(w1zfsd;a_ z>=S{s{KFtvS8l}Cvg_JlmPvab}!7fr~hanILSg}QAd=6O1}`|n6~nRp|yh<{>f z*S;w~rl|4{CY-Au7{4BPYJG}vx{`W)Sa$Y)Zg0NPzAOK1&3O?QacSuF-&A5 zysv6{yuM;=6oBClw42o$Wt2CaFg`kXV9f*DY};NFDu%1rHLhHL@gocfnz5Xy-r2FV zopint)CJ~)136*YNnzYd_5JuV1DqxLZi?c1^`>`5Tz#?e3`&=x-wU*S`j|7~Nqll_ z;)@&riy;RC2psW^ws}JE!-%n8JV#GE@ZSoI9I++6es@3RIxR^OA(f(0PSHVEm`VYx z>mYz&lq25qJd(M_Y7)2IXj`|OKuM1`AqIV80J7IBb8t&#c;j_OYiiuX{~&% z_lMzN_zL@7Iv2#8-4HU~!J`-Ymm$Y8&ohJIdrI@*cv{u&52Pin!#L$DY2v#rP&-wY zB53HigoJ$^r^*f6N2psfklVf0>j3M}s_Z*kGguHzwNBVomsRFgZ7jChA5e`FSwnK~ zh173_4!aI+pywVY5`BzC=aZfJ!&6$fyfwYk+3kNB;Q}12~ z4(S4r6%}ykxHTeu^@>^5xi@NGp2gKw#C<(PHnvM7w*?r4?kBORvn?RMDQ+@!y)L>T za+hXrD=GW2rPuYLQ3zz(G5HVhI^HqRdj=_U&gmE7oA$S~1l?MXznMQ+^jVTls;x2e zdf;G!nb+H)t11VNOpN0O9~nff|H9?uH9ncxCHw_mN5g^F91Tq1#(*WDhvPIX@ib&) z330r2Jo{p7m~Z}k1d~nvoCOeG)(>3tfGXn9$!(Kx7>-5U{rJCm*HLyc2SfclM6U`b zEaOu-oJ5_|Xl}(w*V51F3^x^NXzKhoJ}hr67BTY-Hg7sg?ME;5p*gsq3uVlQM1S3! zqcn_&?W1OS{6my8N9xPd+m@&{>1~jGRj&2$=rn^XFA^N`Z3*X|k6YQwl^emnijI>{ ztY|y7`u0j*zERa_^q&j1H@9_u_3puO*P$j`itqp8uCJu* z-jiAyOUh;GCT0=S2(!n3t|hlp=rzIkwOZF7#MY&`pV~cBp(==l#ysoTyzs-5k%u@R z&Xr5PGB4rsuk+u%rnxX7O-E0$-o52qo0Wn9Ymg2A91eJ)e0(w$`NFS&Eo(ujI@0x| z{Ggj!Of|A!&(i|eg?|D)8YkP4A-;tsNt#p-G)KMWP)q1{=&?nB;Vj|$E<9gDvI8J}`4zgbnIN-?-`mR3jkLs@{^qogz%sCNEr7YB8i^G zDv`!H&H|@hS3ug=KS~XJ^oeool`^!cI#~Pq)m8rQpu$H6&M8-CjRk*CyQW_}48mp; zusR{GSnsrx5043BlFjv085c)<6-tR0zg-p8)O8hhg&RJxq_Pf+B8q?2JLqTRsr@e< z&${Gi6V#xiqmkV!3>`OCh?bV z#Lx|20hkKw!&xnSfOHj`wMd;%k4V3`5I8GRzhu49WDJPKYYvag-x09FtRCsG-u~21 ztA5t^SI0wZXp(V1fOWa_|3H*m(O-5;l6GiBpxqKGB-vuomx%cz<8j(RjVyRd4|_>f zN+@t}^%rzqh~{BV9G>zb9gMHk>dPWYgoP^hvTuRch$;|aPr4e>_-US@=((af!|#5c z-s>@u>#AQ;9%F>FPayc-m`C0)*Gl=~^#xdDi&pfiaDjF*f#{uBEBeO3JlR&E)G07?EPF1XF8GxmA>8s3+|dGdV4 zHNeQTmq$EcDU%cIB`Y3q0?2k{w(!?`?$b-fHMNvdm?4^j%X)KwTr2K0iX*S8#! zY~*Iyy#uV!IF#bKM#U_WuUq+7MNZ;($VE4FP7`n*wiDJ@+$9-^dIaWJGtQpKwGu|6 z|4Q+0Q*$OlN!(Ly_pJMxcGW=_?lttar^&c-)P9Xifs0ENd+86pBE$Zg+{;RKTO_)J zk35UFOe|qJw-n|(cHPB0Q6R5!N&0H+?kFjbsA4iCnF(O=`ypoGKUC~kFONqi<@+Vm zx8%LbqaXe5lHx%KxaVjbqFdnw*S))Pv23=q9;0>OMw#x6s|}i^^EHA(OAa2=1jPst zX$=RGoVcQeGmha8373YNQ^@8TC3^zr^D?eiu*L>F(779rC&~bUP(Mv@$}LmgZ5ZVAoT)winA+-jPp{>Fm~$5P zHQ~gS750$y<;OU#<@ua)V;6?wfV!Kk;h%L<=YZ z#L&-TSB}Ok(4OOC7f%xjKt71d_gp%vZ5#i^*<~)vOR2%{h+VNZ+}=+=o+nmgfYFc+ z`$Cp^;?#DkLd}tzE)8Om{3t?k^*0B(pufz&8OMOkKl7u1!JxvBH*K*U>aIX3QT#w7 z{B9%*d+8w~upFOR?w8q`2f2`naEUZCT}D(&%XZ+wOuC#U832? zUh3Q~s!v^T6VC~;PTU6gU1ywGj_|lOGXV$DGX)0$$8RChbf6KWauaVC8yy$B+Uo3! zwSh{%jx3l*$4z=2{94ZI26Rx9YcNpU{`FZtQJc#z{n^WctAR=8vIedM1! zlY!kzHr63KbMwVCJLr)h@OZOnOM1$!gsUz-W1pDqy${-mZQPm?I}zeMoF>=*bML6n ztxwckwEdzfv74vAAZ}SsSRu6AOmCWJ_Im$!m@@iaAY8>VT4%=Zu#$b1aN4mV2|u>e z5q7O4ud-Tck?S|f5lR6n$s}r|?*631!c)6^HGIp8XEH0QQ-qC%Z3kaK(n8p5a^H`` z@oPryXNFKXyrFes_FsS)03SbpUeH0)_L34+8g9A`8S{7k+&9pEV}Jdub5clVfUN~t zT6I*_(%4Ulf;f1VX?zvEc*;}!i-47BAqkh{^4$EacstX}o(xGI9zisboUEk;o+PS& zVAy=zvG!lb!|4ykJ}VkK)Zq+Ur=_mCQX2dk32QvU{Ls@VweA=O!!keR@2cm;$`r&b`x=GtZc=0lG*3nf0ta*m^x&dKV ze(P33k$!EAFGfuq)26O(+0H+7499Y9)B4AP6R$>W&f0vO<1CGct*fEh=Y#9Ga;AFX zd^Cz5$?8a9IEB5TZ!?k?I-9|rkl!pBc)`@;SzT7Ts4(;~96ib2<3QV52K=j@$){NbIqsQQ%DOJV(XiW&-RB z6;km2RodDM!t0O@x@!Z|x2o2nB%908xJK25Bjl8hOQ~2&@I)iHcnyZ6+p)qqa zlchf9W6y&0hGXL`JZjY=w1_BMW(0=DoL(b;URkGUH!=*?r9<%Y<G<5@GB-RSlZ_%|tCSo7^j|`fzwWOAAvQp35Afjf+6tu@AwXMh@#$@!s(28(*cYu)Xbo)N zsOMkcbr+lL$+0TZrBOu05^|~#?Y#i=)|B2nbm6q(>fRab1%D4)T0JTa_&2ePoh}%6Z;!g zV*JPJ+*^d-I|XDV-7=!0Wc^)v)Y1iO8QO@!1ApU@SS=z*WFg= zr{2A)Qefm8{(VkZJZWPwkI$Xv2|vxalAC|aaJ%d`B~dFa6xkCIm_eZL<{B4X;ef?3 zEWYb16&C70P0NaQ-Z4+g3>8M;IZSr}4?K4?KWR(3#g-O=01ac7#iTM8yLifOdk!r` zEjEvxdyQv*9-3krDPR@iXSnY+ku5kH5$UNNDH?~s8{RW8SWHJ}$Pd*gM+Eg8d!TNhP0q3POea)L|& zOa!Gf;h|CVyM%WvO2Wefk1o7vt`XAp^40tFR2KA_pI z2I+>{3n*Z!;|z$*hYdcCwATRc9nU+uKEY>R_R~K}gg=KOSW@b3uA=>#M0bu9D+Nym zoN$dvw{1I8{a3qliQT)fw0^<@M_jj*nT~DiXq&{a3w6IXx8w3V3yr#=8p zj_QJ|;S7xoNKBgTGtKLUDyEU2KIGPz9>47v>yfQnU*g)+&Qkg=eNOpK6F+hIw)&5Ehw#<9v|d=&9`(vCH(uKSIG+3kh!TBk=Gp1| zGr?@v6llzrz>_DXPXHS{hg3SE3bJqLwAk;Bk|o}~ z-zn3+vsT-3d2Yv`rGKGs60G@d6*b+*jFkXQ>r(13Re`A+9yzVV<5& zHM0E0dajyjyPi1M=HihMS9zjPdis%lnxlqICQ{(V>a6Y;2MXXDXP*S(0D?gm^ZAI) zray#h38?v;#+F5s9|bC-4PHGsP}GSXR4%Lw;IMfxEkN6v2XIB(r-%bIfREbuLO0On z=|2fX{G0VZ3FPE&0vYNeWfT**F8ENgWwTevIF-%r=XQ+V7#jQ>~c+-pt{bgI*{a;!gE1O-87nv0VT{LBD=Q5oA8%0-%(5qo(^R;c0+!=CmW0X$zT^u^f{iI(JY&!H9 z7_zpranf%#;x`8+!|InsC{-XqsE}+{SK?R{MYIDg^Q)dE*7S}|Mr*|v(wi*mUP<9E zke%J6;{$%dU7rqRy>HJBssZ!4MAc}b0)yJkO4U(6JHSiSrkS&( z01;EWO)p`o2ti33ZMcT{tyUA9X1(Nd6{^~%Id_dvjh|S$$4lO()4=jm=upb&X75j# z$TvJ#F&SfVEdI(X6EeyQaJFOV_!nj^`2T}ho55k$3I8X|`cd|WJcovsKDi&RN+nnK z31u6w_Kuc4Kg>KFzPUv456qh5UzoKyu`8B5mg3nMm+@}=S~D9AmyQS=4`|PA1UxJk zkd6J3?bJchs`F!?_B#dw{30iUiPb~qLlD%Md5*)KwQCP7wwOWtBVU}%yYBl6j!A*K zX4Kg+;Fti{a&f2Q<8#k~iBLGjdPF(7)=Jb$J}W0l)g`|z+I4Yl81u-Zf9T;`|L!!8cY5*VMAp;ChiZaAtpXKxy z-dN0XRyA17p|JQhJT))JP+n9E+mzR)*q3nk%GNltf5RmN+adURL@affNI|ksosJD~ z@zo=n{w}5e1{BxH5DDsxLn>r0y`lf@hGh&C=WGUB`7yIwDPQ^HtWl-gQd zn{6IB@Yi#lzMB8VwPPtkdMS>mFgPl^yk*=aKFZ&mT5x%`gsyI%Nlg~yW{iiuHU8M> zD7L`=Ppa4;N}+JQFvyLtH1bOeqZai1J;QMx>3m2mrF$_#b|ZNYerJsGAw@)jBXUco z1MN-_i(M1`f(~pzSz{)Nt1Q7oO10D19>Ve_T5r z|HHK-iDuK{l-)V1!WD=!m=Dn{S%{9A+FmI-87ckjd^XRKz9NU|LWj#cZa8Gwb#IS% z^g=(u<$;8Tw^ySoMtM)7QRs->UvrWqLh+xn#3GZs|FplQ-_dn5a)8tm&Z#ao0aZY< zm!bpL58Quc>v&fYQ+lKCn%<2ygZ3Qhg4zg%|AY%t3NiE8$7i-aZdbjJ~qd zF%-WE5T3=kBqIe2zO|A|y4;;CYGN?)X39!VrLLjk)8vcqJcg$WUdbJcKYdBx-@ZiT z;orW5EyvFVE2?B0``TeRZHTa5yf4ok>Q-5C2l!6Q7hcD?)#ZS;D^KYcfiF%ZK};kq zz(q~ZW#^CB&I6@OjKh)DrUJ~pB?y-fLXa_<+$Sm+1lj!)FP??f%+We;sK$X5qSjm) zg-*IGE$c!ZZR_p`1^M1ma>jqjk>Td#aq%~wvnBYlBa(3xrG~oNnba_J)L)wPDl&Vf zW|$TTmw>V@z?-Z%9jTsAQl8I+w}f`Oj@4SFxcty(DNW1Md^dY}e#YcXMm}>c6SruMNi5wY!~&6zi3c+gKSeL)Hls30<5mc- zL&ca`C_I8eZ=g}gEIw_VaDxQ(b|KLK-{J^5bIDM@#`xPy?dEYjHIrDIy^2P%;>CtDk zIq0#a@wii-g9=MtGD2MF%6t5R(ekhN^MzyJI+k_Z@U5iu z;h&xanHU!3_&<_|Yo#(}L#OMkP+(}P$uIZ&-Phea<-i)CdPy@d#6+4vK3y`3298MX z7Or+BYYE$BK+|N_-&7cNJUVb0rr5NCC`0m$L7ccl@W@s7Z zyO(LBe@r`vbJN#lKV@d|nx*^kx1(g&7x{NP0*V-z+e{I0LicVq&CL`94Z{)DR5LtM zYFrVBZU?Z(4^L-abv|)gu-P-Zi5+!fuXH1A-RCf=s6f2Wc##B z0d*GTC#uXEJH~7+AR4i$opDaAeFE0g&kwEuj^9E%0UK)(>D9>FN_)zCvbo<}ds>o5 zHx__Do8R2sJ`R(AUN0Va8`8N<6#2_Wu6IiXrRbS5@YA-L|3~3*s$|sgw~u5jB)7!x ztABDDLc$nbmPj*n>Ei>?-`Hl9bc2y(3mjVb;L^{HZ~Gw?CcD%NNy9eTHcyeNT<9J@ z7#_?N)Yw~TqfiT4-JeE}rFMI`1=-;FV{DAI_Y9-OzMz6zD2&rS_E(Q-M3kJYpT|xc zm&v4pp1!8qY6rLCc2@Qu@#mi^M0&#jIghotbqHnxpuPZJ=v+CLK5W&(u~Jn?XE~I* zNG|j$lXoHCFTNhPv9!5B3+(z$^j(=)l!SJ6toa;Ii#54q2*y{@Jf%P%s)pagi;xd` zL7bH>Imsb4EtfsS7^?!dh>h4XZKxmT`)QD8e|`CX&mSE4g!WNV)My{EBY17`I*%un zXYdCHMJL9qVA`Fb_1d8rWOY~q6B4ZuQ$y3GKM5pWm`hSPCS8(($SJ|`M}Twtm%o37 zmgvGNi`xog3~}x52gJ-cj8mIC%e=x(cS_z|_Td=5`9K1LYSc%=K zRGfy5BM|y;XqO>6{`*+d*Tg92Kbg{MjhlTn7{M&?G6eEX?T|=i%;Hxw#Mtm(>F4$E zVfsH^2wbs4x01!q=N$20T}bapYg^6JC_{8k=~lbTPJsBsi3aR#G0JMVoteN$S__8| zWrloHi@j}c85*AdkwN1%`{@roLFD(&7&=FR*FE+P$RlJ)dWUyam}}?i-Y&4qTfbF= z?8wzO=P$69spJ@?nKy`D6=j?7iNY^0&P(FL@+sCfyRNq0ggD?N1RJ=8V6t|m(bM`} zv;SS=%F@k*uW_|9E?W-TCQ)lC$KXr!{Pz62$fYpj$$6`d1J582NH{7io{45q0$7*hxCh(>gE(t5{LOAwxK%ck266cB!%ZmP2{edcEh3PA@o?4f| zSmhI5{WZMMuN7!{J_lemJF+`e+;o3boXrZQz1~OpdAgjrpZJr&5#sg1_I+5tu~T+P z3Fl$7i4Qr8gpYbTa{^##0pMa32r1&i4bsP?4A|&h`mr#~361M_rpQE~jCoRBwgy_e z{0y+}UrU6t+`x7nB|G5}1pC?!u#!c!PcMHyF^@UiF&yUNofeDzYRPRmTV1!bDkW12 zBcZ>RCxGxaK7%jR>2jw8pSXR5W_%r)1>q2mb0UMt!@p;1`O-;H$1sBb%@s9N>>^k8MKsz)pU8}lo!4CpAb37zg2 z*K~sf!V&UzW-7-$JcIoHPX?)rpEln?__z%EbjG`tNf_0(YfMB_am74oO`QGVPql%N z9#$|0z{lE}79X#Wq*sb03X}5K7#NjuWQsVwf!*OKSx~Srp7NRiwi-PtQu@fN{xN`4eS^HwOSBB za-ef@^mGg-V-x4J80G$=uDUC-PT`YHl$!rIzsvE(vW|O5*u#K)DO)WLYE+jlY`pdf8BUF?&>VMw!r6_Y~tg_9}!BTrjbAg9ahd z6)m9xUDb+vj_5m8;14lA`_Qud+p9Hb>TbzIla@8X=IWbFHCkPkuRP@-6)KchNaUJm zV}}dh-ka8Z%sK9PK6_3MF#PT{Ow3AA_@&b_ZslQQB}h`I@N8j!%8Gwbe-Gd{K54~) z2oe|8LrW15z^~h}Sws}vpNnaR(5_|a4Qh~ieCkw!SQwTKaQi0j$VUJ2rsI*}dg#%! z2+WN=rOc4q?SnOx~PXl6YowM)_;2zu1;Z)re@pq>S|AIqzX^kHC{A6{7`7doS z6`4OT7kx<7vz$SLm)nR9F)L!bF?8sU5fp#+pS7;C{}zJo##4deDW$qUPed?k%_$ox zh#0o}iA}r)H)w|$iLzVC~nA7_E+L@=BV9ZMk}8@X&DnFZxt1Oj5$1sxizIAvdP z7XOEI6Kw$1MLD**83%@DLw|WEcojg*AZHUs9fgTxo;bHSYPva8Y1nd}@3$Fa3@xea zy6`kYYi#tT(IwOW4Ef03L8T+(>Jf9NuU-pyBvzanwHh@GHSWxGnZj7aA1>`z zgT^C%5Vq@Zl~ti=d${pPi!97FmePqOM1OVL_|C!BcGgdkb?vO0ts*RX+lbI;0sswC zeRwv;2_z~^m?W1F^#QaXW8t`Csba>E>NSP88E@|!fS+mxqE;zO_`clQNAWyV8Mg!s zn6_}1e0YuI&I1@Pe8z2<)n>A>d_*WF=qe*5ov;OK#f@cu6IV6d*LCr2?Y0Y6M6@!w zBloCJyXeeFCKGqTJI0J5Ptb1D*S?Weg&6CnZC!q>(WiT~`Uw^f`xKCGJdRf`(yPmP z_o;Uy)1|LgJtr&0VEc!QXDynA3D4$EybMONR7rSWmZ4_$0<+{)>$;NmM{)5F-GGXx zkEGciR8Py8Au230Bms{Gamp8<9Nos-4y1=3|1cJR#;785)2i9vxhc9PJl~2EQMumw z{tvtMj*H+LRKr8ZSpJ4IuH}w|?u1RpK~}VFWnlvaV7a*O>58-cn#*izU&N7)aU7?Lx(0r^noy?fc@XS6oKGjHdb$$3HK5ssW> zEvU{zt+F0w*Bp0FVo$cmvjFT8dN4}je#m&9u;Abm7PLg=aXz}>U{lC{ zE5No+=l5wK$7k%%e*0zH~m1}-{{z0+yse+*9#Fw>@i@Ci1SN-VKFD!(Ox<@Nf zVMSN!d@i(WLTssulq9%%_>D-oj-2G1lf>%JsT4I04s|l8!nn!)%(vCrzNXdc=J%^H zt15{OI~--!8%aM>YUW~VLL8c(EYUG~p-gcQ*kSUG{gjS4(TMSjy{ok`|3uW*T}V9U*f$CX@s{Wmkoc)c}u6 zYE)YvpkYxM49$mPPuiY~g1b>s6I!uEliv1^JTwI^49%svLu1?d2CQI1d0oP)wDqDbVZZb9irL`z{ZKG-9o~gFxw=>WSZLJRWv@#5gx&xrHZ`} zunu3qzl87{-bkX(aX=KMnKExFg1BDEl5$NS2P}|YY*1v!);}oem+iT}o#T6w`v!~2DlcOw-yv1NkT~DnSbTdZ z$94OF)Vbw%;ipBzcCnw`;<1-)7aY6t1e&UkFAH|Z6{xfAHvxyY-z^TiEc76bho zb?H$EL_RDj4J|b3GQm6Q+=C7PsTdUHmrvL{E(Pw{iLOm7N3?Yv$?P-3=~!F)-*-Fj zo|is7Q0XLrC_94}nMdqw(>CI+^sKKv@%MCKsTxoBRWLtYBS%+6J}Q~vl;Pu=uW2=a z4sY7TfiJT7NNrftNMi{{BP1(uU4zf>fE_;pSd>|S(N8>8uGD}D0VCI9*NTVPyQ9P9 zCTwMBq9gr=N*hyv(-b8q{$ggBXnM()T-m!c`gAhC9R_t*x{6eXX8tm0{YRX`2yR=mVU^cfBgTlVICFAX(o&fArm4ZkMMS z)dX*>qauw(vkH~M)to(+dew1z^eRSj4ExZsfgrfwdU#>u{_e_g)p_SfmG3$1eypn{ z$Oy)2EL$68Q|FbhG-e$1D?9YPW5)ezDE&w~_iE-QRrclyF1+>~fMh|9=}z81ULSQc zdgTU8q^!0ONcIiq_lK49*oQDL?8s1`X&-eA;6q*J2F3yG^Q_#v0YNkMuSL$~M~!1=mAluzv!(y?9bCE|InZj|>MEv`c&*p$_1njC!gBl8kJrqSiW7a^Iz^9o6-xFf{ z2Zeih^V^a1e#zcw4ez!P9O`z>#2PNJVX&&KaguTq17b$VX1!rZA0Dj*pJ34cbC~jP zpsAQYXqijk~W+F+~gL1jh)+pt?k~wE=-%ABuYZJ%-%+jTL$7eZ}MfU|GUSyV_ zd4t{CBI@fWF7a9N{G>C%gq1T=gHJ?)EKfxfUB0*I36egy9W2JA!b$@d^|Lo~C$QW1 zUDv>;lhe-0S)rwTKSilY0`z*l2|IA3a>e0lsy-s0XF*Kd@Nd%&Hz=|7fqmV*VH`>2 zhVdJ9qhUOGW4ZYqKF@NYvwHBm$yMw-K)q^uiB)QOeIA7Xs5 zWFScE0B{fw*9-h270uV>zc$q#1xJdwhkCa1O({pasgK)IiCHr07bwh0ZU(S__)jN* z5=PKsvyo~%y${-}?Bx^NTX{&l&V~$Dntu~Zo{n~-zuh%Yxbw^dZJZe-NR~q7zf{dX zTWadRsDh@JLeB!cPTE4XdQ@K*@-k}8fR|MKq06Np!apf((z%EI;8gNF*njcC!0H=lcsT1_1|3x}gITqn;H< z&HDgAtqwbiN}~w$R;hgIsK>LQ}sAG0HpCcz&CMO2gN}(kB zGHH?Qjl){a7n88xRy@)c+fb3 zyW7gn%qyVO<8~8lZ_X?u09!e5D_Kq3`~=kp%!plMXil=%xK8m3X4Cv;b)(m5Lu|l3 zC-MQJV^a{lUrbVo`5Z&2b1Bd$TN&2do zb|-hrC~ZF0W8(BNCNc1M0H~>*%QG{UYMG)32VSYHdtP9@GO&ngcq;JF((`pNlqF8_ z3oh5bc~-fcUqP_30Da&p6RNBpFcGuHMc#tkZCF0p4y^f{-}Rk}UmtCtPjvF18So?~ z7)v}J2HdTA-)=-);4=Yf-4QQ>u#S;4S;Ib*3%Y>4=}L^RTnj8i7LP+c8*GOTuhu-^ zPiox{>%1Q8;qChC@VVSIcSM;zP!)cLj?!Jg7^#6u{M^fqnx z}WOk>~@eyHX= zXngNU1Did~|4IGC$tX64)L|*004{7-zmhni|2fYrIY1{hP$@rpZSNcND73P=MlHq- zm`M-ES#&8JB>62PIXNqSsOM6`|Kqt3Hp8KK9i<db{)?WAAH?Drv>gFnATZNrEO{7ZQoH4-p`Q&}RXbM`+7d@4Uc0ZMh;VGs8 zQLz?n4xG^fw$ldLd=fh2gmi)C71L{wSwv(pI!RB&@YBp_nY^1Ck3l!%3~FdY1eIHl zaanuX#rKth&(o~ix)(lp2}FX{nG&jqpNrU^_nRSa0U3wFUP$&f2A~OZ5Cbx>>iK1I zj$+(*);_U#zhM>p^%Kw)0^i*{)HyMBN{}cmyy|!5GE6WwoBF<^Q92oL2xljCotW?~P80GbElnKVXiw>~sJciK9YIv#!rLOHu{T-Wz z@%C|2DJ28wy-R}Y@&fXU>hN8cX1qt|wtgROr%0i46peh9lB|ykk=f>)l1UXssU9o4u#u&j*EN<;jOWt&0E;g7j`w*v|M(r4y!#$ z$oJnkLw}*)p63g>@;eO4ZCAs$pMzE6U&Dr>Ca|M6$gePWy}*yC~a zcNJ_;;^aE_716%B!MBBpw{dKRS4;o_ia8<6=;Vu2g6uz_Ks_j{n(`{32@wm1?X{!Tj6mFphxCu3&Slg||Z3x?4tZkhL1b#QOa{R<}BAqJ$N@dR~ zTijDIqJ#ydu{Y%Up99&2&E}KAQOP*J#SeY$sD08!HW&wz-$#ANV!Wg(-2x*_kU|WE zpIj1_d9&X_vk{QD#7!*hT;*-X(}*>X-|(TD9E+!UQUI(iastkkEaF@hiaM(=`S)I0t@nmhw&+tWd`LN3 za^hJtQ*P+Cu}`%RY$`zcszhMx^`k+y2oA9tKrF-*ENMJO`l`Fb9#%KHT^{4VwXEW= z1`FLozZZ&FdsA~iPCP+PJ#+i%VrZ92d@8Tq&FeHbe-#e!6uTvG+?#B>b&JijO)jJS z2n$;9J9KG!ax=wht*nvpwoC2%y=x7cT)J{6EdO?x1U|jYM+cW`OHHQ-ul+iFfcNA3 zH0oWcYj3k$nq>d@=Rjb+G%e^o;@|TZofG4=k=GPDo}!^df$T;f1wU zM}X1WvhAEZQrkdv`y=DwF0DV+OSj1M-rJS}xol6A92)!a`Qh4~n8UZZ70$Kw^2yi* z#~{_3WUjk#&6GU8$y3@C*<%{{rHrZeIH4f>+%cq#FLox3ROS=3)ZN`XowwvVD5R!; zWIQ69S#6f{cR|5dtf(pjzaKO@3Oo%Qk3D;Il$u;`G@1j)6q2kLKB9L}sX8|H+xc!u z&&ZILu|>>|MPSfW9C^2IuD*a>H%M{0qtE)%6YPcFH+1W%>+@)KY9?l=nod0v6GD4J zF5u?o7kozM|LXf2+?hhY%49CK!(_%98L(&|^oB1}IFI$1s-EAMOve>P>18W7eRRMq z!+T#Wa-^j|8LhWj>s;YZkf<5_eE(IN<^W@fwclqBpopC@uj4^vPcMAL>!^AydqxUz z9|zq=v0?dMrySnB(8Vtz-yO^G%ecoxA|ib4$J~jb_SY|mh$VI&x0;jV#GN(y4IoII3#=ULgQQql+HdA>N z#5{}GLUk&u z`~siVysHd*&Re53k_k$%PM-Z~b-rKm(E)NeII(YK`6PW>PCXTI3|Er8xwjJYX}hDE zoWN}pGM4q8=(w^7CKK(^ZvBDHvi8F2sJ9QnCpk)P`^?}=&9TayKay}w`kC6D-c=ZMY&W)>nk&^Kx+TnQ59(QQ<9bOrBI4Om(|LDvrr>-0VfE@0%CBp>ReHWh2xD4kEVdhKiG_*!o5i;_tw`}h40 z*NTciMf#?X82D~7YRjqlOMcST*XWs|7q1icN4jag2mBUU|8a+2kJ}CFrUMfdD_LVLz3zUHfIY*lXTGQkf zu#6|xjd|5_I|@>=Ce!Hzzo(34amocjGnV3M^hSBG50+jq z2iK^gV@xPvQ{OlEyj3r}L@cqp-6Y=_TijKPv)9TBv*3r^L}p z;>-iqO%%C3{yWGeBsE`f9KgBfx90w=%1lb_cxx(hP_e-DdQ5TA${v0;!mJ3lGgG0} z_|!+{lRNu8GsK95)x&CD8p=W7e(i{_=)_m$a7o#$f76_Y81zp+dE9p9Db_*8dp&?> z4`7U6?-L&GZyz8xi#m@KRiRqUlsm8Xx=!Z=A{_{RGVK!XN{HdFWLkKVWV!ETJ1n`+ zI)4cl*bcwrIcsj*r^(1FxVVu3B^vahc{_C!W`5>Wum6auf!l)wdj346@GH7+XPC8$ z%6I7IHfewMfXkCk=d%vcX-dZzOW#-Q$IV8E-d*8}l~nbf9s9kF<`54?&tvVLa%y z_my~9Caw{EHI>xXJ>#99Tl8aHN=qfjC*f!ooMK}jy%ZYS8Hu9ai&lAr(ovC{V)vF7 zcq>w|tl;Vp|FBIf^+D#M(vv{PAoG$diT~C4+>cEA{lpjO9-RNSN^{U!yVsjz9)NAM7(Xr}@ zDvp-MuHHjaF}Hf86m%$HHyBWM0D5v6*eZXQj_8vLBHW|Vov+79RFzcZ`M^xp-B%TB z)?QF~p33!5R&=p=m5>k~+JDmcTN5#KXYsIR14izaKFWs^6|p+zNzd#`fE%{I{IqW5 z_GPnT^Nv+fIPa*oxvf8#AyC^+mP>czQwZfjP1iw%!6Ih}?)M+As9T)isj8^jZ5APK}R=8^P$A>m@GS7CM3 z^Rv|JhkTm;jq#^Srhvk?S;`%`qk^tD8YTzXB(-98gq9c;uKACcq2(gGP?w5G_#@3V zV$QQRUFFwW6Y_erXT5Ya+OC)W|^~k zEF`PIdx^X0egojK+fN_*yD%zpZNp~S0?pL24hDJq=){lcNnP+if`@ggHy4jmVSbQp zmTE*ZM8?f+LTW}M;q@ZiSca~%aCTk3uNq68o|PZjnXopJ)9FwhB`rZ=<9U;psMXGA z|Eydz#PE*)h~_k%^>S;V8Li3Yw#Wy5tMf6VW%V(z7c>RDutEM3l1DTdr(VGHquZ9+ zvr-awiOK4Aff*dXNljHGWgum+m=W-*eDw8PK(X>q1Hu|RZQ5`uTpd8cX3Bk!tKHCr z#YTkh(dx;MNP@Kw~tdTGZ3eyuwtYLvUj^w zE9G!~Si1z-K000cS#Z+p>XZr9S^Ej|yiJc~Tke$;uEIODO_*+9mEqeWiK zu76kV=3biqSnTY5l<&~|(B3Jrdj=%?R)nKj0CXubQYS2UmZq&ByPCv^JUA?e2J-O@ zlhtW7|G?^G8sHp&&f`At$IDGhrZ#r8eN6LyGA$BO)_1Vy`uP5q-TQcjTE^;L2>Lxg zZ8axrDm{N*X2CO_c7Ko4_Hf6PaMAM`mmA0~_Q!@@0Y|6VeGK|%?^ZUvfDg7Vu>|%i z($C4r)N8@nDGXy{!r%UTM}OwighEbbCa#5pt1M9D%ysPZe(E{M9z;$ftavEFM-*0eH`%k7tzXah_2fOpX5loC z{E`236$YhCtX<#mbTA`hXmu_kJObvVdCYfPBZkisz(HEz) z!mHh@C3OeZ*EZ=Qyf^Amtof{|L7WP$1_lTCvGkw-+rR40#O#Ge{8*sE{;MMWK=jg- zFBrMCOtR(TOAHa-CJT#f2nGXFL&K(&HI@tRkgVU{-xE$|&bdR%_YYbDD;ocEj_gC767l`?@P7tQ&KjraJl zl`zcPbNCJF?Hg#KVrJ2d&K2atNIgVixw;t*Tz_=C!q@~GFK=*vg&2CKH9&W$wX!>< z8D~W%q{ATIGbNeV_IZg;JECRj`U{4$tbh~3+qfC}_~H}PDMc;jYAdTq{X^3w2G23Usib5S-Q_aFkdyTIWzr)cgoMy$pF-4}~me!Lcgd zu?;lVve~%Sdy|u7l_e4J>zoAzKC>SgZ!FRf5~9-=o%iXkt~UG4RbuXi`B2+RY{e%o@XvUUil`q` z*EzF7s?8hM-7_J7&AIL9F`T_jTKJ?I!QC0MH~AQn)~d#y9<(}B8%KVO{_oE2XjNJ$>pA8I8IGmOgf|BJJM`7 za!;~JD@89wQk1llv&8n{0OSkM1g0L=UyJ23JMfTjB_s7$su=O*fvy=FCgBzo?wLNWo>!pMA zH(+SgK=SAH)*?>xaw=1khH-JForY2J>M`BaW%i7Gt(;RB!Kr)~Sz-C{crEpDYyUp- zcSgDyEi}tUEf4LUghx{nrHb(L<&{*zO&Be!VqC$3-lEyV(9lRKOIA~8*0=S%MnTFg z*pd*(e8bVSl`Z#RQJ}iwv9W6ZK7a-pTndL6j^V346^73F&{1Z7NPH{_Aw>Y_6hc5R z?!zCh6o*dsk;0BqFijmI4cGhB@Tg+xylsWp+h`AUsQ77jGaOYb)4^{v8>jm@Tzgp^ zO?|?EBSdW`mp`S0R9y<7iW8e0qOdQNUl8AvIT>TmW&t_A@>^7|zCX(+4C2}AY>}A= za);(YK0tfQt;e=?BI9EA2~;}d@6JQ#=Bb`W3L(c-mYMt$=nhJ3r`VQ&)y;#0sw!m6m{qP&!2gR#_bHb zrd%q8=tNF7mUv#&bJemxb-&W6bt%Tuq+wOMzbGvaQb7q&4B;bQ-G&&J2^5kV<5)i3 zd7r5UxLJ%xi_Tf+eM$%2Itw&Vxqs4Txqx@;Y#g$Gf91FHesTqY+;}p5z*&(QgW6rN zuxsCq9Jm`?^&-p8-2{?f;8Hy({lTQj8qO(a)YlKs(Anlzf_@pl}bmi2pY1~8d*cq&qkc6*ETDe6&M&&@5oqqX*XZ^ zwZfJJM%jXRhYQv)Cy@Bge5J9hN$maY%!O7fkIzkiCm8y$)A_i#y}z)8bdDrkeF?GQ zH$8U*M;>TLRUa!pw%smbdDliO8`l@Bvn%a2EE}&4z%k^yjd{)C-I<>YUWa`?JpZ2V zT-GN7GDNBMpWx)qvfx1QTweJ-%qFzlxOkIpQ^_^`!=o*;yDlfSn4Ft<+jMraYadNE z7DHNAmO3A9qoN=z58MUT-$N7`%nmb)2DIylC|-F3V!1Uazkz30<((?XigY{Kkf=~t z4r{7(=|-h#ab;;mLWtv+6GF4qU$kedox%I!(iN_@tp%%shoz<0-(nAMX9#-{uW)aV zUS9{VE;at8oZNmwmQl&`A-gjiXNSlq!~-68Xjin?@q)iw1`Y9|_=XL63NtdeMCiM- zLus*+jcA)^2fithR#^%teRgc}bEe$!Atpc%5gb|KXzO_lZ@PH^KU{_n(2Z9iVveE8 zxtDiccRkZb4=qS3ZNUSx?l*XBtE@qHtW~QV*N~~qMLLchRv)jMn@cbixMag zdt1TT=I~?KuwPM7K_2hcb0F(eCMPTV#xGnI4~4Q>ux#gfM+7tBugNA;E7ool*QUA9Dk7xKxIu1Pp*TFAC7&j5dJ9{;Ly>~ z3}lRTuFuop{ew>Yi?svaW~6t=udolcFFeRAm6R$GXRb1jX$SF{=?gCH%=C8#~n;!<1{@;V+457g)7AOg|ZEQhgn@ z9?!C6SdhFm&JPwtg?9uUm0l_u&Marssj@ z>p-d#+)ttg&Fl1viwvpIO1&CeO`Vdu_13+4#oHaIlDek}nR-04m+NfMYnhOpyx+5{Ajxd2!&x+OEc{Nev?7iUA zw>^3sq$+M14)2KLxpY^-=`~!2C4+$SImTXthJ@y`pe)3Lj2q(Qv?qusUCc)}+DEro z9qJ|*>iMp_6w3K%9%&A#I6{$V3-a_D4uO31f#h~{ryxi;xw;tv?@0jP$s$`U4Tdu4 zF9KAMfoYt`P1ulOr!CHe3t-~iQy@pN47H70d37K9b8R7t7&n))QlZl)jbeQ-H z982e*&_OS&WyheB2b))4%MEVMCP$A6)Erl94RDNB-ikVg!$WK~!2ZR%h%xvz)y(6K zn)l(uZT}yZbIoP7ZO`|#I*IJI3A==mHV=eAE!#%;ObKrC z`Ssp-V^Vd^ZNoMjFs8nzF3XJ{Llwuf+UFP7*DiD1l@R$!%|V7vd$w0kQ1HRTrjtXJ zmC%cuiXRgq!-=mQJAU2j9URtF3O`Aj?%AV^~4;Xn~uM5N^kJ zbOKx?SSW@E3w>n8GnSP; zl938}$YTDu*!HsLNAJ3u#;;JuA+gyecRVUp0b8`^~%G2 zZu@(=)9X(XDYBpBOJf{Xc5a-Y>F+j|-zps9)_ul2oC4-b~WCPV{`={4bEiSzgkK?cTq|ouwpQ*IUoA$+UYiF?sPKxJ9*1|7JXb#qo%Sa z!|~G(!;xW6_AccT1tQhh8VHXa*Gn4J{Q>jry(_;D+-ib*Mnu=G^;w7MiQ08ptA%UmIauykr}-*;jMx0 ztVsv-;nnPPn3=hLsqf$%(E3<%{fT)8I~)2(LXCUV6P(a*YH|OFgeuYZ(gGYTSqC zm%kfQzJ^XdB1IIBxhFEf<*GTG~e3fD#z$bQnRU#09a-dB0e_G2~V?ikBL znrc51(1ezLyNr1xbH9TeiDJTJklSV&<$T@mG1RtB8Xmdmz@vy|NxI8L0|k|%lEa^n zk7mY}>v4HZ%%`zEK%F)>G9e>A7GO)rbW;7G#9BRvBk{uf#1lrGxxDY|{LaJ&G9l(} zxZD|zU46io{+|l97^FMb$vf}snn+DPtSu#K(VAynoNlmCRO$7QVb6>841P%628N^! zZN4Vh+x+!l_MotTsCN(0`J`gvavuY^Z>Ht-Z0wXcE`&%u4tI*AJARPZ4aK?LWeYfT zJdl2{SzUyA&zuK;AFQw@@fPWP=asUqHABt zknfT{_vqE_x~%DO<$3@I`fT?oELHD>zEay3yq5yM4I|ufIP&cFcPCKvGV9g3WPQt~ z4$!J7qhU23nN#Yq4r`#LPs%Y|SN4b-b}@OS?fl7R;dO3j4?yl=h{*3eL88HlxOaEJ z?`Ndq??XWLb)jyBNZQS6>(rX_t2WgFfPW49_(2RLETWHq)IiZne%*-vS2tZ%jBWSR z;Pnsg`xR}w;QzV6btVNm3=J86lsaZoiGO&lS&2sgatOi*9$vfY{Bl8f0iT& z;$aFd6q{=lba0l%=$mDWuy`>Wwja8)+7%4)@2a|~BCIRn8O~Ed*~pIqK6dmJ;mSEP z*j)knMfX%*%|DXB{lmAQX35+YPc1zS6pQP71AS z;>Gji+N=eX%Q)e*RmpT*F8RJ=3M=9Q{t)GTBexn7ZB{Mx?yXh83}yx2PJpmlw?;}n za3yG#?l7M4vf1%<``_jH)7o$YX>YLLtPwO0CN2r5D-WTmn;Z#$TslHrW{aeajfCtv zTi(j#rm0(RyST|=XO;UHElI%}Yk;gQP6rMw44RNG8$qjp#b9OjCY^nY||^ z709pqF4B$qq?wi72At)Qq4}%2$1Y*@i+)@~#%#vE$HIBfY_4r)U|nS>PvB+0totX% z$mY$*o8A{jZVvM)HSoHm+EC8X=0$Uf<2#vSG{Ob9TdGJY=enLqr4xaSNDlK0v5J9S z7hk%Mb>$&+kx6n2G2`MZQY)vi`C3FwXc*t##GK7whstqn3KbH#hxq<}`8w;R_rOGg z+dltsz>mf2pOo(%LQw*Z3!lwiiT_|7q7qwbbQ2|+IF2%cRxoVhQ8F&tmekG$KF^(l z?OIZ3eCc9lJsGn#G{qfNnQgt|VDEp|)K3F=8%rMfjg5)ux2jZ)bzqVD(A(oF^lbPl zuJqYZ%Q=?qh;PjbKRmsBo4{))7P(_q8=`;+@)oPEJ7ng?u8Zv-OGoDx27}D0931DTA(c;COm9{ew@0(0&9Lby^KX`1) zmTu)vCFJ(qczsm|pdt^urMP&$SZ21r9@-R3OTWOg3+aVbq7`2FX zWi2{wGJR}Ld!X`Y>AkzPl^czYBvF`RGIpEhT$hj!N0|W7dH(@-)=%d8@d$cX%aiW4 zzv^%@8%}jEjOYD5A9Ryl3-=Ovyo=+UF+y3i!QCC4j)}ACKV_n2OM~!n>NGM_6Y>8v zbFqP?K_Z(f7-O``N}0?7v}v#aqYl=I*=i`Pizo~IE02D^gneGN(|yvMnXkDZtFG{{ zzw9T3g@9kMrm^W0G>0q8yYVU?%kjNPbE&V*5i&^ z?G@etLa|eR!Sw4l5dA?E%Dr6Iw){i~t@BlrU^0Q)-S&5WcKO>SwZb-&3juc*mv?Q} zcSc{X8sS&oN7A%u9`E2fxsVS4It`C^e&elzM^oB&DMr7)&J>y~%~f~~TVE;9*!J}| zhWYEEJ zFX_tM;Smz*Z*m<;7y0%W)dVZYT(Z!6tlLdo&rBVmnV#I5-F$_QZl!XcbHAlJeSs5K zqhRV?0GW(DSa=V2jau^0j$y_1_Z93fZV)$vaUPItd`k{8bga0jNIDOi?1 z%ow(L*$kMGVGNR!d^8W$WiJDk@`{qRn?5hAoQ~7gPA&B<&Cq|hwmHuIcXkK^&)GAV z4^<49H`$R?l7XY=Tg@Cn0m03@Ww@lv0qRvq zsw1rRxu@Or$ra+yRm)Z(*sy?p=gMQJ*zNXx3Acu#cVfN}{IYQZ6mH|A)wKW!VVazE zFK)Yga;fm%aaNm*sqOub4}5rF)1-R!ac5{!P{&%OF*b9TVHCBRp(yO93|}&z*(NW* zqn~Jk1Ue#&oEuQAAMh4mA{&4=1)7|-*ESsAOh+sb9`OUGCbr&V-7-$iws4$Pc7k5r z(A`D0P=ndt%n>y*0y^&PB(-LDLu1c&l}p_Do2p_(he0$Aexdd1jZFGQxbD{$AJ1PFA_|b%_RY4lF^x@^Z%Hf=sf_vIR+Kv zwfyu9fymhQeisO9mXT*|RYQo4QQW=CgqyK(Tr z#_D6iIl0v8hWbpvB~U84^X0+8F2pz{LjA|D`kk;G=c+W5ltPvPR+Y66QTTm7Tsx}C zxcIXN7YDwPg&-MDRo*p4<79B%I^~F_5q|~lumkbZbIV@8l$tJTRQ|_~_`rU#VL+=J z2Z^b6ed%7=EEfiST#LNarNy#bd91%2I(b)-Gn)s+k|uYGowNa9n87vSWEX9q-DW$L ze2@;=@Q*@gdDAm*P4b*TnSuFCb+zFr?Vhe z_1j>$e8(G#A4|!+ecldJ61-c=^e!v>?{@z~`9!2`3S1Q+EuXg@D#d3B4H{V<`7K;( z7b`-)!see2ZkGCYhz-3QQv(Lt*5eFT;!q4P*YaUi2dCqzhJD^^umrzVtld@IzjlYL zqQENedL$_KFRYjP{>Y$~-L|{aGyb0XR?X4h!F=;;uGiF|xOv9bzhuY$*4y~LWQh(j zeB^apXxZ{GM%B9pv9g0_hTnbPeTk3|ft&a^7t2><5z3JYvo>nIk62oCD|hlBEwEt z?ZBl9&w_h5yJ}<)((4Vra62%v2a(5m|KvTj?8ZTcD`B$dy|CRk4SKo{)SDl0Z z<$L@XMqdSC#|6dCxenr=`ZBB(`jLpW>#KnNi=?VZXbA z;TG=3EUCCvJqvt6R$gop1Q*$y(plTWo|-n)Hu$%P^J>5dbD*8%W~|@Aj?IH}F};mk zTNnmylexv5L%_)VZ>VtlSz@F&Cjil7wp95WL7M*F;>mG5VQ90~yu*H)&f3-`clymG z3;JWI{_52>ri@G(VtF+iXL*VGja5}#Rtf%D2|>1?Y9wvB2S5}(;K&~mK~oQLFz5+J zkY;(rPEqEd zV(jvQd_@1S#{WB5u`Ro4V?0yoK6#1$%-XDTLa9Wg>8b&^uINeJ?MNTalk?$cYM<0YAUKJGuqcVt7`r&g+B(k zYSx~h(G+iA{9ekF{L@=PmKH%*#W&za%v{!0jMyBhob|BHN_GjsX$B~OsZ@p+@cc~i zv-Sl~F2)#}LgM+n?9rk)9|wvL{7GfF1;+p@wxFIv3q<0a-g3C(myHQND@kHcnU$H) z%Eufx9zLHP()8=Lm7Z8cv`_0~YdKi$`h_2zukW%pgAylLgTZ)W^68e|Ci+Bsd7bl8 zyK&=j>RQ?mqW%cSfA}?ryN12xzRk zMLt*$guX+8962h&Cg)gqZLN@;kDhylWI~ag{uAUef1iwFU8}+w$`7Sg-Sa-`Ymke2 zM{L6c>IFeFYln@;k0r+y6lnpnV4%s)LKL8xk5*+G*J!`J(~lMS+)V@BVTbVOFpOrE zzv>H(X{)19`PGU_`t>1Hu@$@v4n8jTAm(^xdS)z@$s|g~#Yi9COKVuG#hsGaQ}&`y zbw4<&=6FOjNfPi7{17tNtJ8ock{U|bD6cUsmd^oYdO5Irp1O{eQlTufr{q(+OL-?J zI>7h+x$eM(qS0ql4yH$dVaJI8U(zxKs%-?ROkvVJr1edGU+;`E(uoLo8pXz_WYe(C zwRDf>Tvwc~r!@B2UVq)XrmPZUo7qd~QMzKUHy?a2qu(X3rC+74t$8pPLXv;*o|rpm zvmN>JJXa0%_nD4>m2|98Qb7piA2om_#q1W-7;7-3o#4El zTi*Bft7>QIS6NN|jA77i-eJ=lV*z7pSxZ}vYg=@OF_Kr6bB$j%iP7A*^M-vAce{hs z%~zq5R8OZ{+E&z{#+Gp{W|yc|1_|`-614qOwNS>NFuBO?mHtPSmSH>fh+l)eOwXy` zewKL=NFiFV1Qs+Reg3XQbHvy2yUGi*eq4L$weyO%zJbf0;Q;r5;#>1XGrVC5>Iuwj}x|g>0UdI(NJ0G<_N_b`Emm z8lV@CiywFXZ1%OUel1A0h58b;yuRX~i9iIvx;g8Mt5aV=BGu4Tqhe`~TB%Hn>|)Q> zsRx5xm2&rK?#P0C~lm>!?;r{9r&S`hRmjid3wLgC{}@V+46?3g9T zm%d)Z+O}}1w6xcECNY)uEn>DP5x8tQj-qeU-i}VB=Y2!+sS2v}o_bFC{z)%0+5@}# z-!}HJg1>ARwKAS$WK_O#1ulOL(S-if$iMZvn%4HWK?kX)ghxw<{{NTz;Y|IneBILo zYS{sSeZ4KUt@$KqCJw);D2)Gc&%)TKYYcMT#yBSM0`=oS?xugt$*@O-NVg1n6|rnz zkp$OMi)bk#5Z%H@n44mZ176(wJD2n<^|F0|Tm3eYn6)eF^NxSh*c}iei!;WwL3PL9 zqDc|;_nr;pWtDqEN+b32HPZr;&)1>j*1m1pRm2`9-;?>QklK%Ce(L+!eQKM#p5k=l zE+M;0Pvu92x?8$OZwDcXicKuj>h;*uHpZbo?0)6{SU(CS&x|$wNy_w+?y-Fa;9N%} zZ@fNjabTO@rQ^j^e4>TACR0c5dEkgBWSdrt5n{i7#cBV==DvC9)PPye;+8R)-2T^U zrO|xJ)g)Bx{e9QVgoD;jUr}PNO0l-TDF{?dPO9tYns+Y^dl&Dp?wKB4!#=%A`}WKH z+?Xm-rr#egAMh>y8{qv@-_48{W5tBIF55FhRci?)Mfs;u&asIK zzaZNJ5Qkh>b$Yi{hd6urah{jhM^XZSz z9LTyIsVkxVd={a55fk;`XV=PAP6gsEa{t`ozs?WOG_(+59Iy21in99zj)wuCccM|_`AKv8FjYEFGDvrK#%%M8voaH!Y4o}v%>FI2qv);%uz}XQ|*r+V{ z6$T)C?5@w%FIG;N{Xc}{0Ny(BLHBXW5)E{IlS5zkD_oPNcJdfIcLVl+Uk_raaT52o zZd3>EzUA`9GCtpmB+RnzK;}yD z+ij7{RqE)KAj%y7)eB3{IHnC-GfgV8H#x1xq;_`p10!?fnm%?)w}fzj zP(shh0Mt=3|8LB$+zb<_zFvJKx_q_%7|2b zK2@djQhg=tuS$RUN`uzCmok~T?7Ft&RiP%cJ%8)3p1s9+AJr!|shl0jJ z#;R3tcU+*${V$(uYPHAqir!O`0IyB?e^j4fCe$PdEzzibp^YcXEN0(?^Rw_pyMqLb z9yd>EFe15Mx}k$6(OG-ky@a|T1(@}x@bsVdwjHXZ4ja0hD?_iE0 z%=t{T_Vyb}?^Bk?S>_l5dZP1t+1*bjju;6UrGHT$3bQEJ@}Qd>OM(*Aq?KZorSGVM zeN?=YZo%M4-|(r~t`xawxi}$V?vplucp~XkqQEJrkd!LV&K^i9M4hc*XVtqQ_mYJ) z@6$h~k7$e*4yA0eJL8c&uilRR^T;4-eSQ||Odj`@9-j-$+RX_HPF(6_gKz+1Q`bGv zfeyGsaQ@R_)5B%lqbY^%SyT$K+`sMZ=la5Xj{e3oH?gm4>t44w2!BUB@5s}wi=x#} z!JbyIO(l9arMkv4sA0U_S}h$+Q|l>-4k3|2**CTJq5V#+!{rq!^N5M14K+@GZ#_)@ zQBt%Dx@Njoun*_lJV~PdVYUc>zu%)n;3NArOg_nLQ*YDO()mu8WV+qTWb{b=!n}J1 z`5>syqs^mB=%3dbqRjooPAiFh2&&MY6boObS-tc&t>x_y3iYH#iau9hkmX;|$NGOo zANmCAA?)K<`ftviOV%T+*JH?DS}K+OFn2LU^FvndXn)ld-p4!GQfJfE^w2Wi31d-x z?20t38^;s@y76!d2Dg4A_?A_>AY}GGk-H-Ep@0L=*{j~~Mm8R>+SIxv<`fP3*eC}U z)y=L|=|ox>bdT#B(BI#EnJ?@GZ$i%|U|NCru|2*u=I1}4Py5;*3_SJk2QQPb(Qzk? zW3|tIsG9$1GRLF;gejI9pu~y8$ORw<4pF&fT7Wrx_TkxLCFt z!A%(R@#5zcO8u7er)4;kc>7(q`XkX1a6EXxHE0g`}&a?2ob8M^ja_Hz9@6)d)0&X<~20JV#X56hWo#$Rn|BhFD|{;0RBwURG5n)_acMCU!j3 zekHOdyS_Lu>9+D&i5LuQjE@U)HPRqih$lslY+Hvf7)r#j`cf%G3_a}o}4(d(b< z-n|;6!|HnG{-qiGD_?j$Q@d^U^WlGLcDAM@bR)s=N9M#Sge%Ha=E4mmE0a89ktXO}7tCJqW$SxC6j<|KgV+NFi?QpIlv6fayoE z%&~P2u}{XQJh8JcRB6X%254eayiW5cPoE9_fM7F9kKT%72|luXWhzPJ@>k*#UP;e) z&=d^o|9bC&!f-5#j!iCYgq%}gGo&&$k5nPb-Db_a-XLuRO~M*W%@hGc(k))XY{@JV zBo$k(8n~tUuOTG_rizJkp^tQ~?i-|-hVjXXpZ;Np9e$UrcGNwO%l1KZonvE(#w&&|n( ziGH`p>Yrd;mF_#3C` zI&Sqj|#%~E_k+Hdse`0fi#WxN$r^^{~(HPhF0}(Q6D{Obfl-djA0A8L@ zKmBo+wYT>}>d4ldsi_?%nk+^e?}^0q>-y!IW-P*%cd0}P*D%6K8kxV8=rb=4#JZ0dRT0^1{g%HpA&#!s+6 z|D40FaWzA_lW$<`Luk{o1xG`U74P*Q>Y=ds^1x45{`a*jOQ0cwFSJ^!rIx!uhcYH- zw@VH{?1c14>mL-i&wNw#i0}IJs>S%&u{_bdCvzz(nVkC9F)iI{n9qDBnSHKT>`I_3<7V@Dc&xlGLJFFnCBA4}be_1U;t12p>2t_oQ?nMUpDko3)BQVZ$RlSB zPiIV~Tt>ZXdI>0JW#6PzpTdhNRNhXN zPVXwfnmt>>r7E041`cbjb;5VyxE$+48_b@`8?adYJe1|8TA?3>u%3aB3rlNVbX|oD zl$V*;b|BLz$4JEN#f0cwy>F&nF9DgHkQq%K2C;{gSP3pgzyl@mmMKWIc$q7#F`*x^ z`-u+u>3S#alJ=3Z&)sDy^Cb%>qg|^0ItVW&`njk2uW`p}=MOR4L-EoLmuLoZ?Ku9I zxtTX_g~f*)b+{*(xj)%(!{fs%S4TW}X%XF~z5s{^z@xS4WppEa!DdpL1Bl)nxtW}S zCzx(mgTX{Gt;|8EJwOHg;Vk_8V;IX`Ej1z>J2KD3NKf-9xO$FN;H>ROYeeoUq~sdS zruEC!qQbIXf^kM_dJueRo7JA^KCyNAm!XSDxkzQ|iZ z?lAa?j~wX6YDr7b7!8-lXcmc&#V*Jv-}8wtKNt4Vo=stH!rdZ3go!ly3ID zL=EoYB!Q^%3(eu7S?r%(;b9OKWmS*P_{nhiI92X?P9@l>V%Qo#oaYIg+&Pel;Jk0A zGme889X(TM?@dOb1IvkNx}5$YbwuuUoB#FuxO(h2{)}JG3o37y?cxTE97%pNlGC4% zvNS2l$NPzj)AzRSd;;;+YMHQKf0@O#m5C^LP?}x%wy!&={|Q?cEj0!EO6Be4nE9@wlk}gk0Csa)M!m+3TMike3(Ci_Xx2W-7a1U&rbVucauKeyS1j&K^ zTwl}PR;Y5NWo{aDvxo~7t%+T7F@I0bB*pcHs&zew=(LLH_nDZnoR|P%9&% zK;$Z8NSz;B-ho2Ko}y+cQ}&{|?mlbA?fUTk({ z&>LMvb3}J&iSVfB?y+$CEG!Rb|2XhMeU8Ov`h^JRs1!GVd;PkKzd#FGm3R8wTaoC@ ziwfiQd(|U_$(7X%nnE@8?|%XnxI8dFMsfkMkr=1}DB`{D8G5c!B& z>i@KLmQig++t$XVxCV-QTims{I}~>u3It7XYjG*=7BpzSwCCLW z*}M$y6C#oCIClH(G<9~!ax{uzu-UOHlq z14eUIkka@M4%8El12s92I!eB_n7TwEqvFN#NDg~~aQ*r#ANt*Bgqm#wZBS-HgxL~A z;c(&;n_p}j=}6g8Q7WpgW8-c<)L#Ow4M_tRR=(>u4l#hLH6AU-Iqg;+_9^s32(r|6 zuj*)%(63)^FTbjiG*#y7{7o2~Yrxm>MFc~q!NHJ@MQAdZa(lix8`W{@|CTbC!&qea{~CyxSmM@ z^BJ{|xj0T@Bp6NwrB+VCa!B#mmF9T(3kB7m4oqKeg{BaM2AZmhoYi+Zm_s5XT7<-U z*)P@~uNX$7=t5a0m_l=j6xcv*3_b}=sDF{5D|#p>8nwA=oijoC>r>$XowDfRmoo6^~!8w0B}C{F8Zd?6x< z31z!w`~`rHrf4}5-8&U$+jsyc?Q<)1%lhW`M*+8phBR!3cHgRk@}cbciI-_mfAz;3 z&fY2xk==5pXV3uUIrM#;dvRYz5y&Y@8t<4_;3Ie5jIg*hy=a{Ix&iOhUJqAGu?WSd zuYxhuT~vs_Sf|sgu3}HJ!gD;XTr{|_zCeUoE=6|=$!>fB#~&sZ?Yfw)V|GyXRX2{) z-YK#%nb-NLw8$nE?U3J8T~>Ixi~}`Mv$^wJ$YDObT%4QxqQUE*_p?(Fet1CeJuENL z*T*4v;m$Vr4To)Q-cDGvqL~F9tya8B=t7IbvU42cRemgBXb*s~ zv*t1d8|RKE*JT%P+I#TrU`IH_eZHjlX%@-FKG|!d;r!+tjs=x_&mx|vBMny;YG?tL z(r;9UC+b_=I`1*dAKNQ?$bpP77q^)wkDdP-A4Gtp&t}_E@?T?($A>vR2bEvG4OMW^D_`+Rx!at5^Ex+4Tx(EwAxw8D)8)nwwH@L^Nu z(*q9%8j6b;Sv2#rvAwtzf^7*?XcV1&NDN92T3>mfK)K7*GEmAmDoL`* zOUl+h>5x4}|8h;GZA44XW>}0+ro1s!4X!?@Y)(F4$v#RJyh5o_vtD};6tQZcAnlR# zf94Qpes9xnOExtSi6;Y-rBJ_XFjmz<2-h~TRK*NN&;rc&!W2!~O62nm2C2PY`JTJQJIGXBBdY~m- zcJMRb=E2yx*lwr*T>kXUan{Z?>qh2BvH>%2aOj`R1ajuktBtH{72&PTzRd6Oj`3k0 zdZ9h}3$0-^c)jHV=|1J z!jKPdAn_@mK%MB|sZbckBfQhQi&4c8a`>=%@2T=nb0l*TBxo~sX{N^CS?jwGu5^@C zLm8Ag`U>|cLlJXXyDcgP?Ku3c;)I^rYyYZ_0~v(KOb0A54Ge+x-hwr&UE%!s*#0_sA&=ti+m#w{2fD2U#dE!UB6 zevlj^;faV<^8qD6D$S2Gm&jA-g($jF=PBvW0zcYE||8ZFMpSiGaw|gY; zYjpFwTNAt1-4xC!xbBb~M&*JdLudXM85+0HKZ{dvqyB>FDdK$4(fu~f?k2~duPWQ# z7PX~q*Hal6v^#^YLA(P*UEn}DA!U>wpfJQH1U66SuNGDXvARm_VrtY2J2FE^X(;Mh z7sml&Ygc^#;f1~qpqy7;XIra(UDeJPY6b^}3jYI!GKsE7&`;)Tyk%jrA2QAiO%sIg z|0~B->mBCHr9y5p_pqi$Dn-|LFN)vl>19wyVC)#K17k1(A z*-tP64mzK+guYwea52j4s#=VofXM=fg4o-@j;mO`3zAMt{|&Il{?r1p zJ;fh;Mjh->t#YUyoEYf_?QRtZ$li#1P;+vG^3X^tFDFrb6BMwa5)yMQYowe*jTh&5 zF(OugZkOn+M>*X^`AjtU+&My(6?`c6-QqvQP;~=&PtoRdmW)^Vr^6398A(VPSf6Fx zYObc6X;xT>t7z@bg%pe%PB9Vuc6dxT8%83ktSzgcvjK>oIMidnA7&QWYi1Jwq#VvG zDrTpN4nOLNlJDTY1{IqJTJ!y6TlI2g7X^)z5S3=9AQRW9p_H}qtC6|Wo?RRMX|WL(0UdQN`d z(ER0_#{g)|rSEgA;>~o%iDdqsZ`JUd!hBrLJ^k0p&+?wBLH219^5`c0pvn83MwEpe zGd@pcGkiPf#sS7sGC&icPg@>JQ2QLHnP#dO*Z1LifaXV5 z>t}y@-0s0E51FxJ8p1~I*f2E~?4H1AEJ;X44@facW9*&Je)Y zyLv_ZF%5|*OltHQ7CZ?EyDU5b;vJV$Eu;E5qUU{0dA;}kI7)R>9`rMfk)>-x`4HrG zaB}6tvs~2J?VRvWeaK^?2X0@%BbTg2QE7yF4ee-qGfmwWzNQEitZC!aZEJsg1ILCA ztn<>SP}*v6?c!&aPBTL6aJo1lGHrcC7~>G4cSgso@8drua0@(MV+GvR^FxB<5XlNH zUl53^yn1W{mkUkLhzQ&wxdz$nV}e%tvL+$!p}6C3`$wI?Fv`-qH;nQw7$erdT=Bk0 z|17IHLA0Qu)tI#9^KFH5Lo*Nmazjbr+|WkeiR^u+>Hi>mv^X{wmXK^r4Lv@q#FN>2 zs$3}3q-8`GiiOo#^O#motH1phdhuV&ApI^!e zE=HPT(dogu^{x9h(dkzcS4uJAsNfP0xfXe|%QsWF9 z{13Ay z&VAEHo$WO8p#G_KO?rswRTesRKN*sjgL9~5`|an9LeLhk9_~;TxF6VY_Ur`PfGC7r zDY0I=Bg!l?A|48d<9C+1@Ls}oNfSy_v6!Qo#-|FnE)g2-`OEDQ^a1>=&s-bT{R7Y9 zf0{j&EM(Q%cCNHri_!frwI}>ceQN+;b&k(U&1*y8?FyXQBlOIMhpu6)mXm2I#`NJO z#k?aoUZG8U3Nvq^&rs#7rq*3Dpq|e2k{GT5P`;6aIsZ0mrKuJwC6(JV;T|t0B=O|l z^{r?lTPPE%Vj8M#!F8&r<&A9-aNRCT_m#xgL920US?}JW4^Hi&eja~2&-uKxEb(v@ zhJPgX*XrON_)8ZxAU#336P~;23pb4H`~6p=gqzp<%}9vf`raOYm^9x|HG^ZY)=jL; zHhBD=RlAE~eK{h%VzQkTiP!lE!g+J6{|t|TmuXe56PMY-<^s(|tx4bI`d)SCzk}jPPfq6Y_gB5tP z{inXRS=xIegv*j*cgsy^z!J4Br@%`J=Z)nh42eEaS}>H=w_-Awun?J}J*W_kVkET%6-kK)Lzk?U3>z0v09xwyws1-IbctV8x!_b^ zu3dS69{O606M!6tSvV*ex`r?l72%CoX)4>FGPFN<$e@u*Va~Iu{R4@%`R4m&F~kMD zz)gd=d2$h=b>21d`3>^DM+6O?p$_>e8c(-gCRPJ$xDtv^>5Z z3lr9jfqiN{q14zMa*s=d#?Br>TQN9oW6aMgxTW>osr=j`g}2i}6Gf2}@}b9WixL=b zTthw{(NNZ|UPj`<4r`x}20Sh=I~ZOme9Pw}ksF+=U%qF|O7g9*^g&~N8{C?z2GZ$C zY)^Zp8hCDNpjy{{5X;0!eJ6a9ro#F8m(tygXWi<1le?L_Uzx83vBNfcBcEy)SqL9m z@v>Bk2s}Ww>{?5x!A|i&iwMCjD;k;w=im*;f+PZ)s_Cf4t#j28-dn?81E12eTBuYB zHZQN9ZNq}9;R8>%S07LMN;k@4$5(0n30&-Q_6f=tuBX#Cpskh?q!Y)<#Gc%cAQynS z1Uv>h?e_7o_KDNw#`y_uo)x&*`QE9lA*Rl_w7V+~!HtpdJ5R%RnKjN7ym~mUj^+^6 z^(+-eE!Z!m!qJ4un;i^jYGdfQGw{v-^j#&neFzk`mxm@NPZPZB)S<=iA)uU$ zBVTkUNAvzggC`NL^@GwQ*IYR%rP| z);{$&?fSz>e=N{M`+_o|!-ceyBwLXas|v|jB% zlQk#4{GATPkN)hfqwsT}*h7>!r-TAb`pUUU1RX#EA-_Xd5X4;5zx)Im+zk1 zVK>Wf>j1~FF*txH+RtLtJ(an=N7$D`C@fW2>|s!D6Yu2ekrjEp42~8xamD)Agmut| ztbfX@%wh1npewAQ3;d4bV?C|LoCcl80gO7&cl|8NuQ%p)f4@gCQ2#E_HzS52Yrq+6!KfB%s~`T- z?$)fx#A^6?f3dUmGWle!#u;64f-zY7+@zmhLBy@YZ8eV_pV?&Hu`71P z4cX39jLwg$DHE zy@1W2vgwhxWY=Q$KnoCZ-3fEugxOTo~qJkDjAcX zi|Hs&=rA;UL=XB|va4*KiDAM-bzxxXinJ^g@H%*iCr%5S>x~+T((d5SPi+AvWy&gr zqGEf;I^SjzA7~KVLm|Ijs@f`^Goku^i(M?}glJ(Nzjn&wltqgGju~&CSF|5c(>J@P zL6NmBAj6sMM=_fLk;8hr&|^k5%qeSN`e%2KdtrJdqBAwMx$b@Y8AHu_QV8qzdlY>u z_luJ6D_oX4m!_f{0~)pBSFz6BDwm<=2Dcl!a1p~oh51YPbFegRyEi4n*0!#v8&<$i ze6);2NLq0;X(p$U#_+q(%Y~PCt~IoG@iH)5?>S{{4{(9d2K_|DEh;*0{x(0GBu1cV z91!EU56_xBJLvPWk=Fw_8U*47DWgHw$x|IbN`H>*R;?2m_$7?TOt!d4%q_NP$}*iz z(#51M`_?YEAmkzY&{D7y82B_Kan}d<+EIEE7|l|v`|a4UW|1yu_XxcA<^DY4Ah}C{ zx>9VThk9bun#h4I=?i&eDo_zdH#+U#DNz%Qe&6T5Kuz>HDLd^(ZZ4biJHcQHvN$TU zUZ6mDIzFT%GCrKz9ql7q7@>-Lc$vEzh?u`9n^xK>_ig?X)j*m6%$rUG%I@+JO z&y3JXq;gVNX<(>NAlN^+F*?tY47(iM*bEE4-IKWe*%l~lBpyZFD2Rh+rip5><(n7H zzM%I_qCPVpFtqW@5%M>46mxydOnMn;@R!O1pIk6jcit0?eowEWr(3@7d1X6`3c1_! zj4kFU!5*%hNX*iB9%ROU8nFxt_p*XOnZ5RZtv9P+-m~ZJx+Hhh`Wv=2!e>s-(Z^F> z{DQzixTp=W4OIh2g1EZ_VY#w`M+tXm?vFg$6hz6bi{@}~&IN1qlEUJe2i z1-ycKijQFjgAk}6Jo5(nS>ZX^{O%($r;`feyQ$8!3qnIoudXaV4PrCO`mFX&<~3NLbWsPj?4Z>-OMt^zZ!bmrpI_UlIo}KeD&y z#J#qSG-VvNF3lMYadup=(6W|ebtj^m_HWW=f%P7g6%ob6#2H(FP4rkGxquHaT(?aP zx?X)RM+1|DnNEFXcP<2sHzAQmXlcKvOMe?h$q77EC;>QZELH)T^BS6t#d5WfK}co9 zvm5vb5C#hYLKnlVdRVXA5l5v7&z&P88O55{h_4rQWS8YvZD?3IYd&^uQW8_}e{#3= z`b6Z~OxcZgQu$c=GXtiJ_TCNN6lkMa7_X5L^Nu~T9V3GXMAS0T!Kpzb(a?}8F80x} z!A#owTPq4%SG1BGJ_Qg2%`EVDKK64{wbxn@rHmd#gigQ?dM^i2mP^1Wv|*#Q(Fko= zBmzHRKkVr`!ruyoAOBi^7ci(+frl5i z;eT>2VH&iR3!DBKc39D!G|Reb(xVQjm@<^(=M2m?HmPjNM-dMuPHi0%dK>slOrlk$ zb{^~6EsRocIetDU6bp9qQv&|~Gk19#a8RFb9Id+-vm(5$vT11gj9d6PqXdw;EHYKj zmAHua8Qoy#Wf}S6w;X~Zal!g3UuF}+a6NfU-;ZJ z@hW8aaFsI03=j+wE!a&Yl7K`ax_J0Ez>d8Z(JSnPTiT8Zad69`T4Psfl7VGHq1*Rw z1w*%2C2kl0@K2J6?^dQH)sqh8bSUD#6|wp9D@bcG0W|kg!=rB|r?TFf)6{EC3Fg}& zahgMQe&;o}Lg=ZwHGvsnvr^4z5T?w~VPgS&x1r6i@p)JbP<`^0?9QYgyn%=vuq<9F z#`dhmEMCC316<)<-u&aAkah0G!uCtF?%W?F_XnO%+{5yW-u>){X0tggWxVA z0a8bwurGe9ERV2d1wecSxC8Rj@#%x94awdz0*Nk;`w0<8_fto)52OX z`PeUs4-O0P@PPi|!~;m&?{mT*UC`Wb{#D_?d|y!bX^7RDm9LX`lzJ*7c_4D#Pt$Au!b7wgQvC(Er6O+A6A zu%M`E@u3Yw9&fsnA2hZs0Zox{U>SS+K~`p$UHeR$E?hU1^wu53=2k>FS@nI(Rs5GA zpzqVH*$B}2<*Av|(k5O3>d$ZWW`cpmPHVp3@VQy8iU=j>1Mbxbo}e2`JIQbF8-Z5= zHhw|yT;GM&Q0dxDsQPnpr4N+n?r^`a%~5tEJ|kDa)4XYLLI!~7YT3l0uD2~BkZjkd zT8zSw*0$cqiG=e&GuJ&g_u?J@hwsLZv$KM6PU#MNY|#k2pVX)}Gi^MnOc@wgnra#mAE7iExMb@R>=0;T>z}@1tx04m9Vf5Z z65ao!eglSIg1`Ygv-%70`e=}tTOo&S2_7YXtSV-?Ksa=0^UnNnVsW?LFIy&q7|X8Y zM+8C$*rAXNpIH8pL_KVUp>>QoslMHcAkicu6#0WBU9jIrM%nwEtp$h@lYqm(_bOgJ55tTyUEpS)N(` zt1o~#PXXIic-aH;z|S|4ocBEqtK%Gm9a_);FGMxl>kA~dCJOTc`bJ15gyvIBut+I! zgn9N@8n_Qf*)_kr^!#Lq*Y?lyE*R{@K>8F{i1B-6amcz+?eR6Gq17NsWtM|;tpIqE zRAm$WFUd1@CnF3qXdJU_K6@=FoLjlgCbLWAxgZ!e0P#OzIR$n;9{4>U+jXNIH!qm< zObD`27>`~v+l?_7@(Fa{={yBubQMQ(K8=ljxvPFmuJwYAJ$!&Suu+~wRxX5d5T7i2 zsm)PBtkz65?&<_(p6J|1 z#FvCVgPEQ-a@460@v%%if0Ukk6Em=)0zNs?S(3SJfsaytoAC6<3Yb6Ei)O70n5&8N*{GD#+kLKOwiptr9x@Yg-BxL}ijHd&S>1RV})Ae7F8eF#> zlTjQ>e&x!Ik{VRiX&JQy#h7{#Q51R}Qb2pt&wekr?`x&?e3Lg3R!2z(gZ`x!&l%*RznyQy}9oECmcy0=i{wQ>O#PWzoQnPclVEjJfZ zhiy|P9{V=1QbL@+A~A)M{E+y4p80fo=nhdidsqN>`#%PSoqZ>22)XF=TZfhMBD1(-HLk15rFEn!9iQi?Tqbl&g0oG>Q`O$a zL;T&!%DHC0|Df$zb!hU)bZ+k&1_(U`Z7l7HLoX#@wI1!QqK(LR0xzhd(qfT4tlYV< zLx@Fs)Cw46ba*z%;EhEhcIN%?*ao~{K5L0Q_sX}qVcwC|+zbCLTDxHxtm3-Vg!ff^ ziXFOAvC3tViFuya=Tky9+%ZKxlGk>hvM@_IycntkJ+ui1KyXevSZpO88D$&LHMpJQ z&h^^}ejD5E6A-X9f0fZYNyX_Je0raFR2X(P343&7mG1x;Q=1GFRtIo()zuS!L`Wo*fYngjB9ewx)z{RaCR#RE$%`MoaHO|^ChcrV^PPaI{@RR@D zM79^m8hkhCLAaM-GkGR#caxjuW(Ue$3_88ArRT;%Pxz3J?%RXRHMbjW<4-E@5F8bX zq~h&0=8|3(h&v--6RZ6-iDW>}WJvB?vai*$CXri~XC>~cRSx~@U`5gjUN{G|+m${D znX??j=6ZI1^mK;lJTtmKR&rq=vjCD}<-JIt36jYb$xnA_`}IATr?I4D{pEIR$vdlw zc-5CrZV#H}6M2WtSBMNBJnTLek%ebk9a7b^LF4Z~B{96Cj%%chPm^JqV6x@8kTQE2 zpR$C-O^|WqZK7AAfphlg^2(`g`1s}+@8R~I^Q3IBU#@2YnS!IBoLx$C_|ADv2d zMMeB{G?$V2`(`iG%fHbe<_{B9a%C4w6=mc5W%`DD$9{PAEsxNGR^&&dH=c|Zro;C6 z%iKe?rjD4_Bff?e!5^J-LcqFnO$r0CPz>*|=&w!;dqp2a_1&4252-kG%C3*?N z+b{+r5a&+xkxukvh*o=?)y+WMm)t@yW+`bB5fVfxxNG@y%vV{^HbWAbN$iiv(lQqY zIwfN?ElU06da*;f4)ndyhkEwKrOGF2tS{ukNFkwpTk2b!JC^HoD~r(Bsi?RUr+LKPy)fVAv;9Z0uoM(9}-u^(mlP!ptJX+0w*hNncM`O7nNMp zob#0$nV3W-;q+_JlVT&v(X!D+qilMc4KeLD`w%upYE!$+8Xh1n>=#L!83E*`nMV|h zTjFk78Z>+`mX`plq*T~QU5#unc@>gh5tDTzS?jN7n95a;#3$F7hw;_LY_pJJhr8fo zs3`jDD$@_uWqolld(l6p94mX2t~q1OoMyPhp;U1~UtC;36{dL8+U{FP&4WbdMroK) zZRwn=xq6jh3`%fxC`kh}UUxjEHLM~J==l$Gp?%vTg!_%9Z zdYYfs-r*2^#nr|srs7kDGJ|t&(UwOVvU*OSv(fE?kq-qoJ{hjEK5(P8osRlkA z(U!s&hJjuv=OgS1Iuu|oUZsE(lF$>UjwH9GR5!1Ms@-#1d<<+^sTe#$Dg0^t_Wgn{ zAf3S|`mX~!sdFM$Gd;T&Lh7jBQ0V#H*^&A+= zo~d8m9(IXdV@4CaY1b9nso79TC5n>OLr=GHAyhpO{wS$k6qth9%0y0&7lBs)Me7UF zXbj{0?U2@6aW)yovICc6R>SYr)yIu9+|WM$XUrz$qBVK!k_#6x=GlwxIT628cwe_t zVlVA76MWr7@to&VPAWbGeQXY?~2T~lO#173bdmFjFVPohe2k^-)BUcUaOqsk+V`C_jpXRXi3Vzvg& zqPAOlsbvd@3h@>iKUv4i36wmXEf3N(`Z8eI`U2HKbYJmu#RMaMG%b)BerEUsk6A*f zjZ)uh>9%}y{l)fUfB}QaETj35)m-n_(GJUHs~PtAm)ATf*x~F1b(cM8eOm>Vk(4OW z**ekL>F7$~RU!-VIY0d&cA_|zwDx-T7zvc&{$IWGbhQB9zKp9%N3YaWq+fLW{(4id z#`8=2?Lk8Pj^WD-!p0iLYEPrze_X*gx>Du|r<)`Ot|Z*!MS75zZ1}`q0%^Y;930I4 zy1Wd#iZhtQ=R(1yL$bufrc$~6Zm$Fmk9|$Y$W)l#*5tAh6rqZpVymXZ?9f6);Z5{( zvp7e6lE!Z`MP!dIWl8gb_tm{E9&kUSnaI`W{RW7$js>uI5?O|gD6?))hfi%&CLU=K zs3=>@K=(B&N4h#CHhhh4V#L%j$ zdsl442~Aw_C9p-Oy(@&h-+=))VB=&LQhp1Isv&$Kc?@rwQbomt_bSBNJn>nY`6>B) zsjrSB2g%8XxB44-9qByNzk1J{z9#izUvLbUh723E=h{~TO`BUD|-&GP5 zQmHWB*u$TOAHN2$aX77e7PYdCds!Ft)zu120&bU@|0FYA_)dTPKz9x#AX^HspiX1Y zb5pPISOeQ(-Z4GOKxTBRjY45Yk+?no_UFeQO@dgU^bOm!h4d{@+o|bz%hpowEr{nF zm&dQ}@u$pQ?t@PlZ0rP7+0<2rgDBI3zo3DF6mBWu(0JK%KDzcM_J}1#m+C=BFu!*7 zzRnEhBOz+r@t+rBBB@%4EF;C~X58Y8{pbc$*pQwFR#wuibly3$e5y*M`}+$imH&W= zO;kr=@G>-4qy0?X9H@f#!H5gx?>e3{oNtd#0)-#Dny6nQ!9P_UK{yP7TUH}TxSkz8 R6^MYKB&RN0BW)4!{{SlE5B~rF literal 0 HcmV?d00001 From eb31270cbcfeed37b7378a4796b28db11d4ff38a Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 13 Mar 2023 16:47:03 -0700 Subject: [PATCH 202/261] fix semantic merge conflict in partest --- src/partest/scala/tools/partest/nest/Runner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 964617ae6f5c..0ceafe355dde 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -365,7 +365,7 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // no spaces in test file paths below root, because otherwise how to detect end of path string? val pathFinder = raw"""(?i)\Q${elided}${File.separator}\E([\${File.separator}\S]*)""".r def canonicalize: String => String = { - val hiders = toolArgs("hide", split = false).map(_.r) + val hiders = toolArgs(ToolName.hide).map(_.r) (s: String) => { val pathless = pathFinder.replaceAllIn(s, m => Regex.quoteReplacement(ellipsis + squashSlashes(m.group(1)))) if (hiders.isEmpty) pathless From abbee0b2bb72816874709113ac31456d9f85d8fb Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 9 Feb 2023 15:12:08 -0800 Subject: [PATCH 203/261] Move nullary override check to refchecks --- .../scala/tools/nsc/typechecker/Namers.scala | 21 +++++-------------- .../tools/nsc/typechecker/RefChecks.scala | 12 +++++++++-- test/files/neg/nullary-override-3b.check | 2 ++ test/files/neg/nullary-override.check | 6 +++--- test/files/neg/t5429.check | 11 ++++------ 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index d38749d64ebc..a4b54dc2d6f6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1474,26 +1474,15 @@ trait Namers extends MethodSynthesis { // Add a () parameter section if this overrides some method with () parameters val vparamSymssOrEmptyParamsFromOverride = { - // must check `.info.isInstanceOf[MethodType]`, not `.isMethod`, to exclude NullaryMethodType. - // Note that the matching MethodType of a NullaryMethodType must be nilary not nelary. - def overriddenNilary(sym: Symbol) = sym.info.isInstanceOf[MethodType] - // always check the first override for paren purposes + // check the first override for paren purposes def overridesNilary: Boolean = { val toCheck = if (currentRun.isScala3) computeOverridden(immediate = true) else overridden - toCheck != NoSymbol && toCheck.alternatives.exists(overriddenNilary) + // must check `.info.isInstanceOf[MethodType]`, not `.isMethod`, to exclude NullaryMethodType. + // Note that the matching MethodType of a NullaryMethodType must be nilary not nelary. + toCheck != NoSymbol && toCheck.alternatives.exists(_.info.isInstanceOf[MethodType]) } if (vparamSymss.isEmpty && overridesNilary) { - def exempt() = meth.overrides.exists(sym => sym.isJavaDefined || isUniversalMember(sym)) - val msg = "method without a parameter list overrides a method with a single empty one" - def error(): Unit = if (!exempt()) { - ErrorUtils.issueNormalTypeError(ddef, msg) - ddef.tpt.defineType(ErrorType) - } - def warn(): Unit = if (!exempt()) { - context.warning(ddef.pos, msg, WarningCategory.OtherNullaryOverride) - meth.updateAttachment(NullaryOverrideAdapted) - } - context.unit.addPostUnitCheck(() => if (currentRun.isScala3) error() else warn()) + meth.updateAttachment(NullaryOverrideAdapted) ListOfNil } else vparamSymss } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 1e72a0208791..6d76ad3cd3de 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -443,8 +443,16 @@ abstract class RefChecks extends Transform { // Only warn for the pair that has one leg in `clazz`. if (clazz == memberClass) checkOverrideDeprecated() def javaDetermined(sym: Symbol) = sym.isJavaDefined || isUniversalMember(sym) - if (!member.hasAttachment[NullaryOverrideAdapted.type] - && other.paramss.isEmpty && !member.paramss.isEmpty + if (member.hasAttachment[NullaryOverrideAdapted.type]) { + def exempt() = member.overrides.exists(sym => sym.isJavaDefined || isUniversalMember(sym)) + val msg = "method without a parameter list overrides a method with a single empty one" + if (!exempt()) + if (currentRun.isScala3) + overrideErrorWithMemberInfo(msg) + else + refchecksWarning(member.pos, msg, WarningCategory.OtherNullaryOverride) + } + else if (other.paramss.isEmpty && !member.paramss.isEmpty && !javaDetermined(member) && !member.overrides.exists(javaDetermined) ) { val msg = "method with a single empty parameter list overrides method without any parameter list" diff --git a/test/files/neg/nullary-override-3b.check b/test/files/neg/nullary-override-3b.check index a3fda6846686..7c4fd1b73669 100644 --- a/test/files/neg/nullary-override-3b.check +++ b/test/files/neg/nullary-override-3b.check @@ -1,7 +1,9 @@ nullary-override-3b.scala:6: error: method without a parameter list overrides a method with a single empty one +def x(): Int (defined in class P) class Q extends P { override def x: Int = 4 } ^ nullary-override-3b.scala:11: error: method without a parameter list overrides a method with a single empty one +def x(): String (defined in trait T2) class Mix12a extends T1 with T2 { override def x = "12a" } ^ 2 errors diff --git a/test/files/neg/nullary-override.check b/test/files/neg/nullary-override.check index 43f443ef3b9f..93e82b74e0ec 100644 --- a/test/files/neg/nullary-override.check +++ b/test/files/neg/nullary-override.check @@ -1,12 +1,12 @@ +nullary-override.scala:4: warning: method with a single empty parameter list overrides method without any parameter list +class B extends A { override def x(): Int = 4 } + ^ nullary-override.scala:15: warning: method without a parameter list overrides a method with a single empty one class Q extends P { override def x: Int = 4 } ^ nullary-override.scala:36: warning: method without a parameter list overrides a method with a single empty one class Mix12a extends T1 with T2 { override def x = "12a" } ^ -nullary-override.scala:4: warning: method with a single empty parameter list overrides method without any parameter list -class B extends A { override def x(): Int = 4 } - ^ nullary-override.scala:37: warning: method with a single empty parameter list overrides method without any parameter list class Mix12b extends T1 with T2 { override def x() = "12b" } ^ diff --git a/test/files/neg/t5429.check b/test/files/neg/t5429.check index 98d13c8a9772..bdd094a8fcd0 100644 --- a/test/files/neg/t5429.check +++ b/test/files/neg/t5429.check @@ -1,9 +1,3 @@ -t5429.scala:68: warning: method without a parameter list overrides a method with a single empty one - def emptyArg = 10 // fail - ^ -t5429.scala:75: warning: method without a parameter list overrides a method with a single empty one - override def emptyArg = 10 // override - ^ t5429.scala:20: error: `override` modifier required to override concrete member: val value: Int (defined in class A) object value // fail @@ -121,6 +115,9 @@ t5429.scala:73: error: stable, immutable value required to override: lazy val lazyvalue: Any (defined in class A0) override def lazyvalue = 2 // fail ^ +t5429.scala:75: warning: method without a parameter list overrides a method with a single empty one + override def emptyArg = 10 // override + ^ t5429.scala:76: error: method oneArg overrides nothing. Note: the super classes of class E0 contain the following, non final members named oneArg: def oneArg(x: String): Any @@ -151,5 +148,5 @@ Note: the super classes of class F0 contain the following, non final members nam def oneArg(x: String): Any override lazy val oneArg = 15 // fail ^ -2 warnings +1 warning 34 errors From e12f7b61d249cc474943fe396a38116746496bde Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 9 Feb 2023 15:25:42 -0800 Subject: [PATCH 204/261] Check feature setting eagerly --- .../scala/tools/nsc/typechecker/Typers.scala | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 26df5bb3552a..9645e2a8bd86 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -734,24 +734,24 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper */ def checkFeature(pos: Position, featureTrait: Symbol, construct: => String = "", immediate: Boolean = false): Boolean = isPastTyper || { - val nestedOwners = - featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse - val featureName = nestedOwners.map(s => s"${s.name}.").mkString + featureTrait.name - def action(): Boolean = { - def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess - def hasOption = settings.language.contains(featureName) - hasOption || hasImport || { - val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = - featureTrait.getAnnotation(LanguageFeatureAnnot): @unchecked - context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) - false + val featureName = { + val nestedOwners = featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse + nestedOwners.map(s => s"${s.name}.").mkString + featureTrait.name + } + settings.language.contains(featureName) || { + def action(): Boolean = { + if (!immediate) + debuglog(s"deferred check of feature $featureTrait") + def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess + hasImport || { + val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = + featureTrait.getAnnotation(LanguageFeatureAnnot): @unchecked + context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) + false + } } - } - if (immediate) { - action() - } else { - unit.addPostUnitCheck(() => action()) - true + if (immediate) action() + else { unit.addPostUnitCheck(() => action()); true } } } From d9ec2618618ae7b2752170c37ca7218c66201ea2 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 8 Feb 2023 23:55:20 -0800 Subject: [PATCH 205/261] Enable post-typer checks to add more checks --- .../scala/tools/nsc/CompilationUnits.scala | 17 ++++++++--------- .../tools/nsc/typechecker/Analyzer.scala | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index e704a7b8e582..fe971938438e 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -14,8 +14,7 @@ package scala.tools.nsc import scala.annotation.nowarn import scala.reflect.internal.util.{FreshNameCreator, NoSourceFile, SourceFile} -import scala.collection.mutable -import scala.collection.mutable.{LinkedHashSet, ListBuffer} +import scala.collection.mutable, mutable.ArrayDeque trait CompilationUnits { global: Global => @@ -127,9 +126,9 @@ trait CompilationUnits { global: Global => val transformed = new mutable.AnyRefMap[Tree, Tree] /** things to check at end of compilation unit */ - val toCheck = new ListBuffer[CompilationUnit.ToCheck] - private[nsc] def addPostUnitCheck(check: CompilationUnit.ToCheckAfterUnit): Unit = toCheck += check - private[nsc] def addPostTyperCheck(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheck += check + val toCheck = ArrayDeque.empty[CompilationUnit.ToCheck] + private[nsc] def addPostUnitCheck(check: CompilationUnit.ToCheckAfterUnit): Unit = toCheck.append(check) + private[nsc] def addPostTyperCheck(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheck.append(check) /** The features that were already checked for this unit */ var checkedFeatures = Set[Symbol]() @@ -144,7 +143,7 @@ trait CompilationUnits { global: Global => def targetPos: Position = NoPosition /** For sbt compatibility (https://github.com/scala/scala/pull/4588) */ - val icode: LinkedHashSet[icodes.IClass] = new LinkedHashSet + val icode: mutable.LinkedHashSet[icodes.IClass] = new mutable.LinkedHashSet /** Is this about a .java source file? */ val isJava: Boolean = source.isJava @@ -153,8 +152,8 @@ trait CompilationUnits { global: Global => } object CompilationUnit { - sealed trait ToCheck - trait ToCheckAfterUnit extends ToCheck { def apply(): Unit } - trait ToCheckAfterTyper extends ToCheck { def apply(): Unit } + sealed trait ToCheck { def apply(): Unit } + trait ToCheckAfterUnit extends ToCheck + trait ToCheckAfterTyper extends ToCheck } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 68a5d4f95eac..1161a3cb404b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -13,7 +13,7 @@ package scala.tools.nsc package typechecker -import scala.collection.mutable.ListBuffer +import scala.collection.mutable.ArrayDeque /** Defines the sub-components for the namer, packageobjects, and typer phases. */ @@ -100,7 +100,8 @@ trait Analyzer extends AnyRef // Lacking a better fix, we clear it here (before the phase is created, meaning for each // compiler run). This is good enough for the resident compiler, which was the most affected. undoLog.clear() - private val toCheckAfterTyper = new ListBuffer[CompilationUnit.ToCheckAfterTyper] + private val toCheckAfterTyper = ArrayDeque.empty[CompilationUnit.ToCheckAfterTyper] + def addCheckAfterTyper(check: CompilationUnit.ToCheckAfterTyper): Unit = toCheckAfterTyper.append(check) override def run(): Unit = { val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.typerNanos) else null global.echoPhaseSummary(this) @@ -111,8 +112,8 @@ trait Analyzer extends AnyRef undoLog.clear() } finishComputeParamAlias() - for (work <- toCheckAfterTyper) work() - toCheckAfterTyper.clear() + try while (toCheckAfterTyper.nonEmpty) toCheckAfterTyper.removeHead().apply() + finally toCheckAfterTyper.clearAndShrink() // defensive measure in case the bookkeeping in deferred macro expansion is buggy clearDelayed() if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.typerNanos, start) @@ -123,9 +124,11 @@ trait Analyzer extends AnyRef unit.body = typer.typed(unit.body) // interactive typed may finish by throwing a `TyperResult` if (!settings.Youtline.value) { - unit.toCheck foreach { - case now: CompilationUnit.ToCheckAfterUnit => now() - case later: CompilationUnit.ToCheckAfterTyper => toCheckAfterTyper += later + while (unit.toCheck.nonEmpty) { + unit.toCheck.removeHead() match { + case now: CompilationUnit.ToCheckAfterUnit => now() + case later: CompilationUnit.ToCheckAfterTyper => addCheckAfterTyper(later) + } } if (!settings.isScaladoc && settings.warnUnusedImport) warnUnusedImports(unit) @@ -135,7 +138,7 @@ trait Analyzer extends AnyRef } finally { runReporting.reportSuspendedMessages(unit) - unit.toCheck.clear() + unit.toCheck.clearAndShrink() } } } From eaac91ec05432d6d66a69f21b5077098fcc76c0d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 27 Feb 2023 14:40:51 -0800 Subject: [PATCH 206/261] Improve check for empty vector slice --- .../scala/collection/immutable/Vector.scala | 5 ++--- .../scala/collection/immutable/VectorTest.scala | 15 ++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index af593e57d029..aa3fac5acd69 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -317,9 +317,8 @@ private sealed abstract class VectorImpl[+A](_prefix1: Arr1) extends Vector[A](_ override final def slice(from: Int, until: Int): Vector[A] = { val lo = mmax(from, 0) val hi = mmin(until, length) - val newlen = hi - lo - if(newlen == length) this - else if(hi <= lo) Vector0 + if (hi <= lo) Vector0 + else if (hi - lo == length) this else slice0(lo, hi) } } diff --git a/test/junit/scala/collection/immutable/VectorTest.scala b/test/junit/scala/collection/immutable/VectorTest.scala index 70257ccf3bd9..f599c3a706d5 100644 --- a/test/junit/scala/collection/immutable/VectorTest.scala +++ b/test/junit/scala/collection/immutable/VectorTest.scala @@ -163,7 +163,7 @@ class VectorTest { } @Test - def testWierdAlignments1(): Unit = { + def testWeirdAlignments1(): Unit = { val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] for (i <- Seq(0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000)) { val res = new VectorBuilder[AnyRef] @@ -176,7 +176,7 @@ class VectorTest { } @Test - def testWierdAlignments2(): Unit = { + def testWeirdAlignments2(): Unit = { val v3 = Vector.tabulate(2042)(i => (i - 42).toString).drop(42).asInstanceOf[Vector3[AnyRef]] val v2 = Vector.tabulate(765)(i => (i - 123).toString).drop(123).asInstanceOf[Vector2[AnyRef]] for (i <- Seq(-1234, -42, -1, 0, 1, 5, 41, 42, 43, 123, 949, 950, 982, 1024, 1999, 2000)) { @@ -190,7 +190,7 @@ class VectorTest { } @Test - def testWierdAlignments3(): Unit = for (n <- allSizes; m <- verySmallSizes) { + def testWeirdAlignments3(): Unit = for (n <- allSizes; m <- verySmallSizes) { val vPretend = if (smallSizes.contains(n)) Vector.tabulate(n + 1337)(i => (i - 1337).toString).drop(1337) @@ -212,7 +212,7 @@ class VectorTest { } @Test - def testWierdAlignments4(): Unit = { + def testWeirdAlignments4(): Unit = { var lengths = Set[Int]() for ( n <- allSizes.init :+ (allSizes.last - WIDTH5 - WIDTH3 + 41); // we need WIDTH5 for prefix, add 1+WIDTH3 and get 42 in suffix free @@ -579,9 +579,10 @@ class VectorTest { } } - @Test - def testSlice3(): Unit = { - assertEquals(Vector(1).slice(1, -2147483648), Vector()) + @Test def `test slice to MinValue`: Unit = { + assertTrue(Vector(42).slice(1, Int.MinValue).isEmpty) + assertTrue("slice almost max to min should be empty", Vector(42).slice(Int.MaxValue-1, Int.MinValue).isEmpty) + assertTrue("slice max to min should be empty", Vector(42).slice(Int.MaxValue, Int.MinValue).isEmpty) } @Test From d50b823d67e25f54d7461528f04509c9b1aa23d4 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 26 Feb 2023 14:36:11 -0800 Subject: [PATCH 207/261] Accept implicit _, implicit (x: Int) in lambda --- spec/06-expressions.md | 4 +- spec/13-syntax-summary.md | 4 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 96 +++++++++---------- test/files/pos/t3672.scala | 22 ++++- 4 files changed, 71 insertions(+), 55 deletions(-) diff --git a/spec/06-expressions.md b/spec/06-expressions.md index b0eb6ac2d8ad..cb6614baef4c 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -1182,8 +1182,8 @@ for `try { try { ´b´ } catch ´e_1´ } finally ´e_2´`. ## Anonymous Functions ```ebnf -Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr -ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block +Expr ::= (Bindings | [‘implicit’] (id | ‘_’)) ‘=>’ Expr +ResultExpr ::= (Bindings | [‘implicit’] (id | ‘_’) [‘:’ CompoundType]) ‘=>’ Block Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ Binding ::= (id | ‘_’) [‘:’ Type] ``` diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 65eb47049519..6e225e177729 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -144,7 +144,7 @@ grammar: | ‘:’ Annotation {Annotation} | ‘:’ ‘_’ ‘*’ - Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr + Expr ::= (Bindings | [‘implicit’] (id | ‘_’)) ‘=>’ Expr | Expr1 Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] | ‘while’ ‘(’ Expr ‘)’ {nl} Expr @@ -188,7 +188,7 @@ grammar: | Expr1 | ResultExpr ::= Expr1 - | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block + | (Bindings | [‘implicit’] (id | ‘_’) [‘:’ CompoundType]) ‘=>’ Block Enumerators ::= Generator {semi Generator} Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ab125e793582..cf8b278818da 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -477,7 +477,7 @@ self => /* --------------- PLACEHOLDERS ------------------------------------------- */ - /** The implicit parameters introduced by `_` in the current expression. + /** The parameters introduced by `_` "placeholder syntax" in the current expression. * Parameters appear in reverse order. */ var placeholderParams: List[ValDef] = Nil @@ -529,8 +529,8 @@ self => @tailrec final def isWildcard(t: Tree): Boolean = t match { - case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name - case Typed(t1, _) => isWildcard(t1) + case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name + case Typed(t1, _) => isWildcard(t1) case Annotated(t1, _) => isWildcard(t1) case _ => false } @@ -784,7 +784,7 @@ self => /** Convert tree to formal parameter. */ def convertToParam(tree: Tree): ValDef = atPos(tree.pos) { def removeAsPlaceholder(name: Name): Unit = { - placeholderParams = placeholderParams filter (_.name != name) + placeholderParams = placeholderParams.filter(_.name != name) } def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) def propagateNoWarnAttachment(from: Tree, to: ValDef): to.type = @@ -1711,54 +1711,52 @@ self => case IMPLICIT => implicitClosure(in.skipToken(), location) case _ => - def parseOther = { + def parseOther: Tree = { var t = postfixExpr() - if (in.token == EQUALS) { - t match { - case Ident(_) | Select(_, _) | Apply(_, _) => - t = atPos(t.pos.start, in.skipToken()) { gen.mkAssign(t, expr()) } - case _ => - } - } else if (in.token == COLON) { - t = stripParens(t) - val colonPos = in.skipToken() - if (in.token == USCORE) { - //todo: need to handle case where USCORE is a wildcard in a type - val uscorePos = in.skipToken() - if (isIdent && in.name == nme.STAR) { - in.nextToken() - t = atPos(t.pos.start, colonPos) { - Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) - } - } else { - syntaxErrorOrIncomplete("`*` expected", skipIt = true) + in.token match { + case EQUALS => + t match { + case Ident(_) | Select(_, _) | Apply(_, _) => + t = atPos(t.pos.start, in.skipToken()) { gen.mkAssign(t, expr()) } + case _ => } - } else if (isAnnotation) { - t = annotations(skipNewLines = false).foldLeft(t)(makeAnnotated) - } else { - t = atPos(t.pos.start, colonPos) { - val tpt = typeOrInfixType(location) - if (isWildcard(t)) - (placeholderParams: @unchecked) match { - case (vd @ ValDef(mods, name, _, _)) :: rest => - placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + case COLON => + t = stripParens(t) + val colonPos = in.skipToken() + if (in.token == USCORE) { + //todo: need to handle case where USCORE is a wildcard in a type + val uscorePos = in.skipToken() + if (isIdent && in.name == nme.STAR) { + in.nextToken() + t = atPos(t.pos.start, colonPos) { + Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) } - // this does not correspond to syntax, but is necessary to - // accept closures. We might restrict closures to be between {...} only. - Typed(t, tpt) + } + else syntaxErrorOrIncomplete("`*` expected", skipIt = true) } - } - } else if (in.token == MATCH) { - t = atPos(t.pos.start, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + else if (isAnnotation) + t = annotations(skipNewLines = false).foldLeft(t)(makeAnnotated) + else + t = atPos(t.pos.start, colonPos) { + val tpt = typeOrInfixType(location) + // for placeholder syntax `(_: Int) + 1`; function literal `(_: Int) => 42` uses `t` below + if (isWildcard(t)) + (placeholderParams: @unchecked) match { + case (vd @ ValDef(mods, name, _, _)) :: rest => + placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + } + // this does not correspond to syntax, but is necessary to accept closures. See below & convertToParam. + Typed(t, tpt) + } + case MATCH => + t = atPos(t.pos.start, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + case _ => } // disambiguate between self types "x: Int =>" and orphan function literals "(x: Int) => ???" // "(this: Int) =>" is parsed as an erroneous function literal but emits special guidance on // what's probably intended. def lhsIsTypedParamList() = t match { - case Parens(List(Typed(This(_), _))) => { - reporter.error(t.pos, "self-type annotation may not be in parentheses") - false - } + case Parens(List(Typed(This(_), _))) => reporter.error(t.pos, "self-type annotation may not be in parentheses"); false case Parens(xs) => xs.forall(isTypedParam) case _ => false } @@ -1779,15 +1777,15 @@ self => * Expr ::= implicit Id `=>` Expr * }}} */ - def implicitClosure(start: Offset, location: Location): Tree = { val param0 = convertToParam { atPos(in.offset) { - Ident(ident()) match { - case expr if in.token == COLON => - in.nextToken() ; Typed(expr, typeOrInfixType(location)) - case expr => expr + val p = stripParens(postfixExpr()) //if (in.token == USCORE) freshPlaceholder() else Ident(ident()) + if (in.token == COLON) { + in.nextToken() + Typed(p, typeOrInfixType(location)) } + else p } } val param = copyValDef(param0)(mods = param0.mods | Flags.IMPLICIT) @@ -3507,7 +3505,7 @@ self => else if (isDefIntro || isLocalModifier || isAnnotation) { if (in.token == IMPLICIT) { val start = in.skipToken() - if (isIdent) stats += implicitClosure(start, InBlock) + if (isIdent || in.token == USCORE) stats += implicitClosure(start, InBlock) else stats ++= localDef(Flags.IMPLICIT) } else { stats ++= localDef(0) diff --git a/test/files/pos/t3672.scala b/test/files/pos/t3672.scala index b2752ce21ff7..2c17a17ff8b1 100644 --- a/test/files/pos/t3672.scala +++ b/test/files/pos/t3672.scala @@ -1,4 +1,22 @@ object Test { - def foo(f: Int => Int) = () ; foo { implicit x : Int => x + 1 } - def bar(f: Int => Int) = () ; foo { x : Int => x + 1 } + def foo(f: Int => Int) = () + def test(): Unit = { + foo { x => x + 1 } + foo { implicit x => x + 1 } + foo { x: Int => x + 1 } + foo { implicit x: Int => x + 1 } + foo { _ => 42 } + foo { implicit _ => implicitly[Int] + 1 } // scala 2 deficit + foo { _: Int => 42 } + foo { implicit _: Int => implicitly[Int] + 1 } // scala 2 deficit + + foo(x => x + 1) + foo(implicit x => x + 1) + foo((x: Int) => x + 1) + foo(implicit (x: Int) => x + 1) // scala 3 + foo(_ => 42) + foo(implicit _ => implicitly[Int] + 1) // scala 2 deficit + foo((_: Int) => 42) + foo(implicit (_: Int) => implicitly[Int] + 1) // scala 3 + } } From c434ba6df22cbf0bbed74db8f2298ae6f3805cb0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 23 Feb 2023 18:28:28 -0800 Subject: [PATCH 208/261] Complete symbol loader on import of toplevel symbol --- .../tools/nsc/typechecker/Contexts.scala | 9 +++++-- .../scala/tools/nsc/typechecker/Infer.scala | 2 +- .../scala/reflect/internal/Symbols.scala | 24 +++++++++---------- test/files/pos/t12736/bar_2.scala | 3 +++ test/files/pos/t12736/baz_2.scala | 15 ++++++++++++ test/files/pos/t12736/foo_1.scala | 3 +++ 6 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 test/files/pos/t12736/bar_2.scala create mode 100644 test/files/pos/t12736/baz_2.scala create mode 100644 test/files/pos/t12736/foo_1.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index a439b111a986..2b97393aac55 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1246,9 +1246,11 @@ trait Contexts { self: Analyzer => /** If the given import is permitted, fetch the symbol and filter for accessibility. */ - private[Contexts] def importedAccessibleSymbol(imp: ImportInfo, sym: => Symbol): Symbol = + private[Contexts] def importedAccessibleSymbol(imp: ImportInfo, sym: => Symbol): Symbol = { if (isExcludedRootImport(imp)) NoSymbol - else sym.filter(isAccessible(_, imp.qual.tpe, superAccess = false)) + else sym.filter(s => s.exists && isAccessible(s, imp.qual.tpe, superAccess = false)) + // `exists` above completes SymbolLoaders, which sets the symbol's access flags (scala/bug#12736) + } private def isExcludedRootImport(imp: ImportInfo): Boolean = imp.isRootImport && excludedRootImportsCached.get(unit).exists(_.contains(imp.qual.symbol)) @@ -1508,6 +1510,9 @@ trait Contexts { self: Analyzer => val importCursor = new ImportCursor(thisContext, name) import importCursor.{imp1, imp2} + // The symbol resolved by the given import for `name`, paired with the selector that was used. + // If `requireExplicit`, then only "named" or "specific" selectors are considered. + // In addition, the symbol must be accessible (in the current context) and satisfy the `qualifies` predicate. def lookupImport(imp: ImportInfo, requireExplicit: Boolean): (ImportSelector, Symbol) = { val pair @ (sel, sym) = imp.importedSelectedSymbol(name, requireExplicit) if (sym == NoSymbol) pair diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index c6121e25cd8e..c5e9b5444bd3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -286,7 +286,7 @@ trait Infer extends Checkable { ErrorUtils.issueTypeError(error)(context) ErrorType } - def accessible = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super])) match { + def accessible = sym.filter(context.isAccessible(_, pre, site.isInstanceOf[Super])) match { case NoSymbol if sym.isJavaDefined && context.unit.isJava => sym // don't try to second guess Java; see #4402 case sym1 => sym1 } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index da84afca58cf..167835cd08cd 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1095,21 +1095,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def exists: Boolean = !isTopLevel || { - val isSourceLoader = rawInfo match { - case sl: SymLoader => sl.fromSource - case _ => false - } - def warnIfSourceLoader(): Unit = { + def warnIfSourceLoader(): false = { + val isSourceLoader = rawInfo match { + case sl: SymLoader => sl.fromSource + case _ => false + } + // Predef is completed early due to its autoimport; we used to get here when type checking its + // parent LowPriorityImplicits. See comment in c5441dc for more elaboration. + // Since the fix for scala/bug#7335 Predef parents must be defined in Predef.scala, and we should not + // get here anymore. if (isSourceLoader) - // Predef is completed early due to its autoimport; we used to get here when type checking its - // parent LowPriorityImplicits. See comment in c5441dc for more elaboration. - // Since the fix for scala/bug#7335 Predef parents must be defined in Predef.scala, and we should not - // get here anymore. devWarning(s"calling Symbol#exists with sourcefile based symbol loader may give incorrect results.") + false } - - rawInfo load this - rawInfo != NoType || { warnIfSourceLoader(); false } + rawInfo.load(this) + rawInfo != NoType || warnIfSourceLoader() } final def isInitialized: Boolean = diff --git a/test/files/pos/t12736/bar_2.scala b/test/files/pos/t12736/bar_2.scala new file mode 100644 index 000000000000..3e3bd2b25cf3 --- /dev/null +++ b/test/files/pos/t12736/bar_2.scala @@ -0,0 +1,3 @@ +package bar + +private object Foo diff --git a/test/files/pos/t12736/baz_2.scala b/test/files/pos/t12736/baz_2.scala new file mode 100644 index 000000000000..187c805bde2b --- /dev/null +++ b/test/files/pos/t12736/baz_2.scala @@ -0,0 +1,15 @@ +package bar +package baz + +import foo._ + +object Bar { + def test() = println(Foo) +} + +/* +baz_2.scala:8: error: object Foo in package foo cannot be accessed as a member of package foo from object Bar in package baz + def test() = println(Foo) + ^ +1 error +*/ diff --git a/test/files/pos/t12736/foo_1.scala b/test/files/pos/t12736/foo_1.scala new file mode 100644 index 000000000000..b7fd1b4355e5 --- /dev/null +++ b/test/files/pos/t12736/foo_1.scala @@ -0,0 +1,3 @@ +package foo + +private object Foo From c1f6a403c2ec6ba9dd5b198a8acb32fb8a5e931d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 18 Mar 2023 23:23:40 -0700 Subject: [PATCH 209/261] Regression test cyclic reference --- test/files/pos/t10639/Identifiable_1.java | 14 ++++++++++++++ test/files/pos/t10639/Size_1.java | 3 +++ test/files/pos/t10639/broken_2.scala | 4 ++++ 3 files changed, 21 insertions(+) create mode 100644 test/files/pos/t10639/Identifiable_1.java create mode 100644 test/files/pos/t10639/Size_1.java create mode 100644 test/files/pos/t10639/broken_2.scala diff --git a/test/files/pos/t10639/Identifiable_1.java b/test/files/pos/t10639/Identifiable_1.java new file mode 100644 index 000000000000..56fe89c73b22 --- /dev/null +++ b/test/files/pos/t10639/Identifiable_1.java @@ -0,0 +1,14 @@ + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toMap; + +public interface Identifiable_1 { + String name(); + + static & Identifiable_1> Map valuesByName(final E[] values) { + return Stream.of(values).collect(toMap(Identifiable_1::name, Function.identity())); + } +} diff --git a/test/files/pos/t10639/Size_1.java b/test/files/pos/t10639/Size_1.java new file mode 100644 index 000000000000..2141e41106b8 --- /dev/null +++ b/test/files/pos/t10639/Size_1.java @@ -0,0 +1,3 @@ +public enum Size_1 implements Identifiable_1 { + SMALL, MEDIUM, LARGE; +} diff --git a/test/files/pos/t10639/broken_2.scala b/test/files/pos/t10639/broken_2.scala new file mode 100644 index 000000000000..86739557d334 --- /dev/null +++ b/test/files/pos/t10639/broken_2.scala @@ -0,0 +1,4 @@ + +object Broken extends App { + println(Size_1.SMALL) +} From c6085a4b230cb914aa79541761e10cddbbe529ed Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 20 Feb 2023 23:39:00 -0800 Subject: [PATCH 210/261] Optionally warn if params should have parens --- .../scala/tools/nsc/ast/parser/Parsers.scala | 11 +++++++++-- .../scala/tools/nsc/typechecker/Contexts.scala | 12 ++++++------ test/files/neg/parens-for-params.check | 7 +++++++ test/files/neg/parens-for-params.scala | 8 ++++++++ test/files/pos/parens-for-params-silent.scala | 8 ++++++++ 5 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 test/files/neg/parens-for-params.check create mode 100644 test/files/neg/parens-for-params.scala create mode 100644 test/files/pos/parens-for-params-silent.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index cf8b278818da..127b75e9e665 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -777,8 +777,15 @@ self => /** Convert tree to formal parameter list. */ def convertToParams(tree: Tree): List[ValDef] = tree match { - case Parens(ts) => ts map convertToParam - case _ => List(convertToParam(tree)) + case Parens(ts) => ts.map(convertToParam) + case Typed(Ident(_), _) => + val msg = "parentheses are required around the parameter of a lambda" + val wrn = sm"""|$msg + |Use '-Wconf:msg=lambda-parens:s' to silence this warning.""" + if (currentRun.isScala3) + deprecationWarning(tree.pos.point, wrn, "2.13.11") + List(convertToParam(tree)) + case _ => List(convertToParam(tree)) } /** Convert tree to formal parameter. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 2b97393aac55..6ff69b02252b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1374,17 +1374,17 @@ trait Contexts { self: Analyzer => val parent = currentClass.parentSymbols.find(_.isNonBottomSubClass(inherited1.owner)).getOrElse(NoSymbol) val inherit = if (parent.exists && parent != inherited1.owner) s", inherited through parent $parent" else "" val message = - s"""it is both defined in the enclosing ${outer1.owner} and inherited in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) - |In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. - |Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.${outer1.name}`.""".stripMargin + sm"""|it is both defined in the enclosing ${outer1.owner} and inherited in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) + |In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. + |Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.${outer1.name}`.""" if (currentRun.isScala3) Some(LookupAmbiguous(message)) else { // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning inherited.updateAttachment(LookupAmbiguityWarning( - s"""reference to ${outer1.name} is ambiguous; - |$message - |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""".stripMargin)) + sm"""|reference to ${outer1.name} is ambiguous; + |$message + |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""")) None } } diff --git a/test/files/neg/parens-for-params.check b/test/files/neg/parens-for-params.check new file mode 100644 index 000000000000..36d277404e0b --- /dev/null +++ b/test/files/neg/parens-for-params.check @@ -0,0 +1,7 @@ +parens-for-params.scala:5: warning: parentheses are required around the parameter of a lambda +Use '-Wconf:msg=lambda-parens:s' to silence this warning. + x: Int => x * 2 + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/parens-for-params.scala b/test/files/neg/parens-for-params.scala new file mode 100644 index 000000000000..92e77875bdd1 --- /dev/null +++ b/test/files/neg/parens-for-params.scala @@ -0,0 +1,8 @@ +// scalac: -Werror -Xlint -Xsource:3 + +class C { + def f = { + x: Int => x * 2 + } + def g = (x: Int) => x * 2 +} diff --git a/test/files/pos/parens-for-params-silent.scala b/test/files/pos/parens-for-params-silent.scala new file mode 100644 index 000000000000..6eb07a5c3c8a --- /dev/null +++ b/test/files/pos/parens-for-params-silent.scala @@ -0,0 +1,8 @@ +// scalac: -Werror -Wconf:msg=lambda-parens:s -Xsource:3 + +class C { + def f = { + x: Int => x * 2 + } + def g = (x: Int) => x * 2 +} From c644c8a2796f828fe2a072bd6bbdedae9f5dd620 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 22 Mar 2023 04:11:46 -0700 Subject: [PATCH 211/261] Runner scripts -usebootcp and require javaBootClassPath --- src/compiler/scala/tools/util/PathResolver.scala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index dbaa6d6f9bba..6ac715ae9c99 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -21,7 +21,6 @@ import scala.tools.reflect.WrappedProperties.AccessControl import scala.tools.nsc.{CloseableRegistry, Settings} import scala.tools.nsc.classpath._ import scala.tools.nsc.util.ClassPath -import scala.util.Properties.isJavaAtLeast import PartialFunction.condOpt // Loosely based on the draft specification at: @@ -258,10 +257,9 @@ final class PathResolver(settings: Settings, closeableRegistry: CloseableRegistr // Assemble the elements! def basis = List[Iterable[ClassPath]]( - if (!settings.javabootclasspath.isSetByUser && isJavaAtLeast(9)) - jrt // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available) - else - classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. + jrt // 0. The Java 9+ classpath (backed by the ct.sym or jrt:/ virtual system, if available) + .filter(_ => !settings.javabootclasspath.isSetByUser), // respect explicit `-javabootclasspath rt.jar` + classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. classesInPath(scalaBootClassPath), // 4. The Scala boot class path. From 9098ff17af67f91dfc290f1181a46af41e425893 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 14 Feb 2023 14:59:23 -0800 Subject: [PATCH 212/261] Revert deprecation exclusion for companion --- src/compiler/scala/tools/nsc/Reporting.scala | 2 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 2 +- src/library/scala/io/Position.scala | 11 ++++++----- src/library/scala/languageFeature.scala | 1 + test/files/neg/t2799.check | 8 +++++++- test/files/neg/t2799.scala | 12 ++++++++++++ test/files/neg/t7783.check | 10 +++++----- test/files/neg/wconfSource1.check | 2 +- test/files/neg/wconfSource2.check | 2 +- test/files/pos/t2799.scala | 11 ++--------- 10 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index fd234ac32866..109ed4fa1a56 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -204,7 +204,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio def deprecationWarning(pos: Position, origin: Symbol, site: Symbol): Unit = { val version = origin.deprecationVersion.getOrElse("") val since = if (version.isEmpty) version else s" (since $version)" - val message = origin.deprecationMessage.map(": " + _).getOrElse("") + val message = origin.deprecationMessage.filter(!_.isEmpty).map(": " + _).getOrElse("") deprecationWarning(pos, origin, site, s"$origin${origin.locationString} is deprecated$since$message", version) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 1e72a0208791..dd0ff2091f50 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1285,7 +1285,7 @@ abstract class RefChecks extends Transform { if (sym.isDeprecated && // synthetic calls to deprecated case class constructor !(sym.isConstructor && sym.owner.isCaseClass && currentOwner.isSynthetic) && - !currentOwner.ownersIterator.exists(s => s.isDeprecated || s.companionClass.isDeprecated)) + !currentOwner.ownersIterator.exists(_.isDeprecated)) runReporting.deprecationWarning(pos, sym, currentOwner) // Similar to deprecation: check if the symbol is marked with @migration diff --git a/src/library/scala/io/Position.scala b/src/library/scala/io/Position.scala index 14cabcc98854..719976c10ded 100644 --- a/src/library/scala/io/Position.scala +++ b/src/library/scala/io/Position.scala @@ -13,6 +13,8 @@ package scala package io +import annotation.nowarn + /** The object Position provides convenience methods to encode * line and column number in one single integer. The encoded line * (column) numbers range from 0 to `LINE_MASK` (`COLUMN_MASK`), @@ -71,13 +73,12 @@ private[scala] abstract class Position { def toString(pos: Int): String = line(pos) + ":" + column(pos) } +@nowarn private[scala] object Position extends Position { def checkInput(line: Int, column: Int): Unit = { if (line < 0) - throw new IllegalArgumentException(line + " < 0") - if ((line == 0) && (column != 0)) - throw new IllegalArgumentException(line + "," + column + " not allowed") - if (column < 0) - throw new IllegalArgumentException(line + "," + column + " not allowed") + throw new IllegalArgumentException(s"$line < 0") + if (line == 0 && column != 0 || column < 0) + throw new IllegalArgumentException(s"$line,$column not allowed") } } diff --git a/src/library/scala/languageFeature.scala b/src/library/scala/languageFeature.scala index aea8a1fbaddc..5d80e7c406d9 100644 --- a/src/library/scala/languageFeature.scala +++ b/src/library/scala/languageFeature.scala @@ -35,6 +35,7 @@ object languageFeature { @deprecated("scala.language.higherKinds no longer needs to be imported explicitly", "2.13.1") @meta.languageFeature("higher-kinded type", enableRequired = false) sealed trait higherKinds + @deprecated("scala.language.higherKinds no longer needs to be imported explicitly", "2.13.1") object higherKinds extends higherKinds @meta.languageFeature("#, which cannot be expressed by wildcards,", enableRequired = false) diff --git a/test/files/neg/t2799.check b/test/files/neg/t2799.check index 866697676936..87419d7ce8cc 100644 --- a/test/files/neg/t2799.check +++ b/test/files/neg/t2799.check @@ -10,6 +10,12 @@ t2799.scala:8: warning: trait T is deprecated: other mother t2799.scala:9: warning: method int2float in object Int is deprecated (since 2.13.1): Implicit conversion from Int to Float is dangerous because it loses precision. Write `.toFloat` instead. def g() = implicitly[Int => Float] ^ +t2799.scala:16: warning: trait T is deprecated: other mother +object T extends T[String] { + ^ +t2799.scala:17: warning: class Bob is deprecated: hi mom + def t = Bob() // warn + ^ error: No warnings can be incurred under -Werror. -4 warnings +6 warnings 1 error diff --git a/test/files/neg/t2799.scala b/test/files/neg/t2799.scala index 2079ac2dc22c..abf78ab17a28 100644 --- a/test/files/neg/t2799.scala +++ b/test/files/neg/t2799.scala @@ -8,3 +8,15 @@ class C[A: T] { def f = (t: T[A]) => null.asInstanceOf[T[A]] def g() = implicitly[Int => Float] } + +@deprecated("hi mom", "") +case class Bob () + +// No exclusion for companion of deprecated T +object T extends T[String] { + def t = Bob() // warn +} + +class Client { + def test = T.t // if no warn at t, then this code appears deprecation-free +} diff --git a/test/files/neg/t7783.check b/test/files/neg/t7783.check index 26b9996fc8f9..b25bd6bd02a5 100644 --- a/test/files/neg/t7783.check +++ b/test/files/neg/t7783.check @@ -1,16 +1,16 @@ -t7783.scala:3: warning: type D in object O is deprecated: +t7783.scala:3: warning: type D in object O is deprecated object O { class C; @deprecated("", "") type D = C; def foo: Seq[D] = Nil } ^ -t7783.scala:13: warning: type D in object O is deprecated: +t7783.scala:13: warning: type D in object O is deprecated type T = O.D ^ -t7783.scala:14: warning: type D in object O is deprecated: +t7783.scala:14: warning: type D in object O is deprecated locally(null: O.D) ^ -t7783.scala:15: warning: type D in object O is deprecated: +t7783.scala:15: warning: type D in object O is deprecated val x: O.D = null ^ -t7783.scala:16: warning: type D in object O is deprecated: +t7783.scala:16: warning: type D in object O is deprecated locally(null.asInstanceOf[O.D]) ^ error: No warnings can be incurred under -Werror. diff --git a/test/files/neg/wconfSource1.check b/test/files/neg/wconfSource1.check index 2e311ca41170..10e2b5233b6a 100644 --- a/test/files/neg/wconfSource1.check +++ b/test/files/neg/wconfSource1.check @@ -1,4 +1,4 @@ -wconfSource1.scala:9: error: method dep in class C is deprecated: +wconfSource1.scala:9: error: method dep in class C is deprecated def t = dep ^ wconfSource1.scala:10: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses diff --git a/test/files/neg/wconfSource2.check b/test/files/neg/wconfSource2.check index e80567394607..012f4eccf670 100644 --- a/test/files/neg/wconfSource2.check +++ b/test/files/neg/wconfSource2.check @@ -6,7 +6,7 @@ See the Scaladoc for value scala.language.reflectiveCalls for a discussion why the feature should be explicitly enabled. def v(a: { def f: Int }) = a.f ^ -wconfSource2.scala:5: error: method dep in class C is deprecated: +wconfSource2.scala:5: error: method dep in class C is deprecated def t = dep ^ wconfSource2.scala:6: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses diff --git a/test/files/pos/t2799.scala b/test/files/pos/t2799.scala index b21f0739bc77..fb951471494f 100644 --- a/test/files/pos/t2799.scala +++ b/test/files/pos/t2799.scala @@ -1,11 +1,4 @@ - // scalac: -Xlint -Werror -@deprecated("hi mom", "") case class Bob () - -@deprecated("other mother", "") -trait T - -object T extends T { - def t = Bob() -} +@deprecated("hi mom", "") +case class Bob () From 9f978bc1204041fae09fe53e2e44c14a293ef5cd Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 24 Mar 2023 11:57:25 +1000 Subject: [PATCH 213/261] Fix crasher with -Xasync, string switch + boxed unit Pretty un-idiomatic code to trigger this! It was the expansion of two macros in the user's code base. This commit updates the ANF transform to deal with BoxedUnit specifically -- we don't need to create a var to assign the result of such an expression, but we also can't just leave a Unit typed expression (the transformed Match) around. In the test case, that caused a later phase to crash when performing switch-on-string translation. --- .../nsc/transform/async/AnfTransform.scala | 5 +++- test/async/run/string-switch-bug.scala | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/async/run/string-switch-bug.scala diff --git a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala index 19924c2ffb70..9d5449e1f8b5 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala @@ -204,7 +204,10 @@ private[async] trait AnfTransform extends TransformUtils { if (isPatMatGeneratedJump(tree)) assignUnitType(tree) if (!needsResultVar || isUnitType(tree.tpe) || (tree.tpe =:= definitions.NothingTpe)) { - core(NoSymbol) + if (tree.tpe =:= definitions.BoxedUnitTpe) { + currentStats += assignUnitType(core(NoSymbol)) + literalBoxedUnit + } else core(NoSymbol) } else { val varDef = defineVar(nameSource(), tree.tpe, tree.pos) currentStats += varDef diff --git a/test/async/run/string-switch-bug.scala b/test/async/run/string-switch-bug.scala new file mode 100644 index 000000000000..daa0020925dc --- /dev/null +++ b/test/async/run/string-switch-bug.scala @@ -0,0 +1,29 @@ +// scalac: -Xasync +import scala.tools.partest.async.OptionAwait._ +import org.junit.Assert._ + +// Scala.js compatible test suite for -Xasync that doesn't use Scala futures +object Test { + def main(args: Array[String]): Unit = { + stringSwitchBug() + } + + private def stringSwitchBug() = { + assertEquals(Some(true), optionally { + val x: String = "" + val as = List("x") + val it = as.iterator + var okay = false + while (it.hasNext) { + val x = it.next() + val res = (x match { + case "x" => + okay = value(Some(1)) == 1 + () + case _ => () + }) + } + okay + }) + } +} From e28268d0f70d19b8bbf269c36f18346f6fc757d9 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sun, 26 Mar 2023 18:10:02 +0000 Subject: [PATCH 214/261] Update jquery to 3.6.4 in 2.12.x --- project/ScaladocSettings.scala | 2 +- src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/ScaladocSettings.scala b/project/ScaladocSettings.scala index 73d4254a4c62..b286b49ecbf7 100644 --- a/project/ScaladocSettings.scala +++ b/project/ScaladocSettings.scala @@ -7,7 +7,7 @@ object ScaladocSettings { // when this changes, the integrity check in HtmlFactory.scala also needs updating val webjarResources = Seq( - "org.webjars" % "jquery" % "3.6.3" + "org.webjars" % "jquery" % "3.6.4" ) def extractResourcesFromWebjar = Def.task { diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala index 77849e07c0bf..6518069dae3f 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -95,7 +95,7 @@ class HtmlFactory(val universe: doc.Universe, val reporter: Reporter) { ) final def webjarResources = List( - ("jquery.min.js", "pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=") + ("jquery.min.js", "oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=") ) /** Generates the Scaladoc site for a model into the site root. From 2cbff24f4c26828f72e798c56a23a2bf565685e5 Mon Sep 17 00:00:00 2001 From: Liang Yan Date: Fri, 24 Mar 2023 10:36:49 +0800 Subject: [PATCH 215/261] Fix tasty/TestLogarithms.scala failed in some platform Signed-off-by: Liang Yan --- test/tasty/run/src-2/tastytest/TestLogarithms.scala | 8 ++++---- test/tasty/run/src-3/tastytest/Logarithms.scala | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/test/tasty/run/src-2/tastytest/TestLogarithms.scala b/test/tasty/run/src-2/tastytest/TestLogarithms.scala index 69992afe9f90..76bf9352ad15 100644 --- a/test/tasty/run/src-2/tastytest/TestLogarithms.scala +++ b/test/tasty/run/src-2/tastytest/TestLogarithms.scala @@ -4,10 +4,10 @@ import Logarithms._ object TestLogarithms extends Suite("TestLogarithms") { - val Some(l1) = Logarithm.of(2) - val Some(l2) = Logarithm.of(3) + val Some(l1) = Logarithm.of(10) + val Some(l2) = Logarithm.of(100) - test(assert((l1 + l2).toDouble == 4.999999999999999)) - test(assert((l1 * l2).toDouble == 6.0)) + test(assert((l1 + l2).toDouble == 109.99999999999997)) + test(assert((l1 * l2).toDouble == 1000.0)) } diff --git a/test/tasty/run/src-3/tastytest/Logarithms.scala b/test/tasty/run/src-3/tastytest/Logarithms.scala index f1e22f568785..b33d99f63137 100644 --- a/test/tasty/run/src-3/tastytest/Logarithms.scala +++ b/test/tasty/run/src-3/tastytest/Logarithms.scala @@ -1,5 +1,7 @@ package tastytest +import java.lang.StrictMath + object Logarithms { opaque type Logarithm = Double @@ -8,16 +10,16 @@ object Logarithms { // These are the two ways to lift to the Logarithm type - private[Logarithms] def apply(d: Double): Logarithm = math.log(d) + private[Logarithms] def apply(d: Double): Logarithm = StrictMath.log10(d) def of(d: Double): Option[Logarithm] = - if (d > 0.0) Some(math.log(d)) else None + if (d > 0.0) Some(StrictMath.log10(d)) else None } // implicit define cross compatible public APIs for opaque types final implicit class LogarithmOps(val logarithm: Logarithm) extends AnyVal { - def toDouble: Double = math.exp(logarithm) - def + (other: Logarithm): Logarithm = Logarithm(math.exp(logarithm) + math.exp(other)) + def toDouble: Double = StrictMath.pow(10.0, logarithm) + def + (other: Logarithm): Logarithm = Logarithm(StrictMath.pow(10.0, logarithm) + StrictMath.pow(10.0, other)) def * (other: Logarithm): Logarithm = logarithm + other } From 394ae0204a8e466bf8729b8b64dc8f2f43c2ad74 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 9 Feb 2023 14:26:32 -0800 Subject: [PATCH 216/261] Do not defer typing empty refinement --- .../tools/nsc/typechecker/Checkable.scala | 4 +- .../scala/tools/nsc/typechecker/Typers.scala | 37 ++++++++++--------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 7b792b89173b..5374867eafe1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -370,8 +370,10 @@ trait Checkable { def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal val Xsym = X.typeSymbol val Psym = P.typeSymbol - if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) + if (isSealedOrFinal(Xsym) && isSealedOrFinal(Psym) && (currentRun.compiles(Xsym) || currentRun.compiles(Psym))) { + debuglog(s"deferred recheckFruitless($X, $P)") context.unit.addPostTyperCheck(() => recheckFruitless()) + } } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9645e2a8bd86..d8b0ae5bd1ce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -741,9 +741,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper settings.language.contains(featureName) || { def action(): Boolean = { if (!immediate) - debuglog(s"deferred check of feature $featureTrait") - def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess - hasImport || { + debuglog(s"deferred check of feature $featureTrait") + def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess + hasImport || { val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = featureTrait.getAnnotation(LanguageFeatureAnnot): @unchecked context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) @@ -3225,20 +3225,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def typedRefinement(templ: Template): Unit = { val stats = templ.body - namer.enterSyms(stats) - - // need to delay rest of typedRefinement to avoid cyclic reference errors - unit.addPostUnitCheck(() => { - val stats1 = typedStats(stats, NoSymbol) - // this code kicks in only after typer, so `stats` will never be filled in time - // as a result, most of compound type trees with non-empty stats will fail to reify - // todo. investigate whether something can be done about this - val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) - templ.removeAttachment[CompoundTypeTreeOriginalAttachment] - templ updateAttachment att.copy(stats = stats1) - for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) - stat.symbol setFlag OVERRIDE - }) + if (!stats.isEmpty) { + namer.enterSyms(stats) + + // need to delay rest of typedRefinement to avoid cyclic reference errors + debuglog(s"deferred typed refinement") + unit.addPostUnitCheck { () => + val stats1 = typedStats(stats, NoSymbol) + // this code kicks in only after typer, so `stats` will never be filled in time + // as a result, most of compound type trees with non-empty stats will fail to reify + // todo. investigate whether something can be done about this + val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) + templ.removeAttachment[CompoundTypeTreeOriginalAttachment] + templ updateAttachment att.copy(stats = stats1) + for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol) + stat.symbol setFlag OVERRIDE + } + } } def typedImport(imp : Import): Import = transformed.remove(imp) match { From c44b8c6b9d9f07e636922887f854adeabd3920a6 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 27 Mar 2023 10:35:37 -0700 Subject: [PATCH 217/261] Respect user target always --- .../nsc/settings/StandardScalaSettings.scala | 2 +- .../scala/tools/nsc/settings/TargetTest.scala | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 610429d9e04f..390ccec69062 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -73,7 +73,7 @@ trait StandardScalaSettings { _: MutableSettings => // .withAbbreviation("-Xtarget") // .withAbbreviation("-Xunchecked-java-output-version") .withDeprecationMessage("Use -release instead to compile against the correct platform API.") - def targetValue: String = releaseValue.getOrElse(target.value) + def targetValue: String = target.valueSetByUser.orElse(releaseValue).getOrElse(target.value) val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions. See also -Wconf.") withAbbreviation "--unchecked" withPostSetHook { s => if (s.value) Wconf.tryToSet(List(s"cat=unchecked:w")) else Wconf.tryToSet(List(s"cat=unchecked:s")) diff --git a/test/junit/scala/tools/nsc/settings/TargetTest.scala b/test/junit/scala/tools/nsc/settings/TargetTest.scala index 7d5523430245..abf6d9ea67c8 100644 --- a/test/junit/scala/tools/nsc/settings/TargetTest.scala +++ b/test/junit/scala/tools/nsc/settings/TargetTest.scala @@ -19,12 +19,17 @@ import org.junit.runners.JUnit4 import scala.collection.mutable.ListBuffer import scala.tools.nsc.settings.StandardScalaSettings._ +import scala.tools.testkit.AssertUtil.assertFails import scala.util.Properties.isJavaAtLeast import scala.util.Try @RunWith(classOf[JUnit4]) class TargetTest { + private def lately[A](body: => A): Unit = if (isJavaAtLeast(11)) body: Unit + + private def eightly[A](body: => A): Unit = if (!isJavaAtLeast(9)) body: Unit + @Test def testSettingTargetSetting(): Unit = { def goodVersion(v: String): Boolean = Try(isJavaAtLeast(v)).getOrElse(false) def check(in: String, expect: String) = if (goodVersion(expect)) checkSuccess(in, expect) else checkFail(in) @@ -65,4 +70,35 @@ class TargetTest { checkFail("-target:jvm-3000") // not in our lifetime checkFail("-target:msil") // really? } + @Test def `respect user target`: Unit = lately { + val settings = new Settings(err => fail(s"Error output: $err")) + val (ok, _) = settings.processArgumentString("--release:11 --target:8") + assertTrue(ok) + assertEquals("8", settings.target.value) + assertEquals("11", settings.release.value) + assertEquals("8", settings.targetValue) + assertEquals(Some("11"), settings.releaseValue) + } + @Test def `target is release if specified`: Unit = lately { + val settings = new Settings(err => fail(s"Error output: $err")) + val (ok, _) = settings.processArgumentString("--release:11") + assertTrue(ok) + assertEquals("8", settings.target.value) // default + assertEquals(None, settings.target.valueSetByUser) + assertEquals("11", settings.release.value) + assertEquals("11", settings.targetValue) // because --release:11 + assertEquals(Some("11"), settings.releaseValue) + } + @Test def `disrespect user target`: Unit = lately { + val settings = new Settings(err => fail(s"Error output: $err")) + assertFails(_.contains("-release cannot be less than -target")) { + settings.processArgumentString("--release:8 --target:11") + } + } + @Test def `cannot bump release on jdk 8`: Unit = eightly { + val settings = new Settings(err => fail(s"Error output: $err")) + assertFails(_.contains("'9' is not a valid choice for '-release'")) { + settings.processArgumentString("--release:9") + } + } } From b5ccdaf0b37b0a41a20c0e278caaadb2dc26cbc8 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 28 Mar 2023 21:40:59 -0700 Subject: [PATCH 218/261] Test for assignment rewrite --- .../scala/tools/nsc/typechecker/Typers.scala | 13 +++++-------- test/files/pos/t11158/CharSeqJava.java | 4 ++++ test/files/pos/t11158/cs_1.scala | 13 +++++++++++++ test/files/pos/t11158/sb_2.scala | 7 +++++++ 4 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 test/files/pos/t11158/CharSeqJava.java create mode 100644 test/files/pos/t11158/cs_1.scala create mode 100644 test/files/pos/t11158/sb_2.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d8b0ae5bd1ce..6f97a3ae760a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4711,15 +4711,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => EmptyTree } - if (treeInfo.mayBeVarGetter(varsym)) { - val res = setterRewrite - if (!res.isEmpty) return res - } + val rewritten = + if (treeInfo.mayBeVarGetter(varsym)) setterRewrite + else EmptyTree -// if (varsym.isVariable || -// // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?! -// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) { - if (varsym.isVariable || varsym.isValue && phase.assignsFields) { + if (!rewritten.isEmpty) rewritten + else if (varsym.isVariable || varsym.isValue && phase.assignsFields) { val rhs1 = typedByValueExpr(rhs, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(context, rhs1)) setType UnitTpe } diff --git a/test/files/pos/t11158/CharSeqJava.java b/test/files/pos/t11158/CharSeqJava.java new file mode 100644 index 000000000000..5abe16cd1372 --- /dev/null +++ b/test/files/pos/t11158/CharSeqJava.java @@ -0,0 +1,4 @@ + +public interface CharSeqJava { + int length(); +} diff --git a/test/files/pos/t11158/cs_1.scala b/test/files/pos/t11158/cs_1.scala new file mode 100644 index 000000000000..da6ce3f5109c --- /dev/null +++ b/test/files/pos/t11158/cs_1.scala @@ -0,0 +1,13 @@ + +// not computer science but the basis of the entire field, the character sequence +class cs extends CharSeqJava { + private var n = 42 + override def length: Int = n + def length_=(value: Int): Unit = n = value +} + +object Resetter extends App { + val cs = new cs + cs.length = 0 + assert(cs.length == 0) +} diff --git a/test/files/pos/t11158/sb_2.scala b/test/files/pos/t11158/sb_2.scala new file mode 100644 index 000000000000..246e32d42f70 --- /dev/null +++ b/test/files/pos/t11158/sb_2.scala @@ -0,0 +1,7 @@ + +// sbt is an acronym for StringBuilder test +class sbt { + val sb = new StringBuilder("There once was a man from Killarney / whose comments were nothing but blarney") + + def reset() = sb.length = 42 +} From aaf7813c258ced468d55703200eb91c56cc30f10 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 29 Mar 2023 00:34:15 -0700 Subject: [PATCH 219/261] Warn if target is set lower than release --- .../backend/jvm/PostProcessorFrontendAccess.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala b/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala index 6fd591720842..cce594a7fbe4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala @@ -16,11 +16,11 @@ package backend.jvm import scala.collection.mutable.Clearable import scala.reflect.internal.util.{JavaClearable, Position, Statistics} import scala.reflect.io.AbstractFile +import scala.tools.nsc.Reporting.WarningCategory import scala.tools.nsc.backend.jvm.BTypes.InternalName +import scala.util.chaining._ import java.util.{Collection => JCollection, Map => JMap} -import scala.tools.nsc.Reporting.WarningCategory - /** * Functionality needed in the post-processor whose implementation depends on the compiler * frontend. All methods are synchronized. @@ -186,7 +186,13 @@ object PostProcessorFrontendAccess { @inline def debug: Boolean = s.isDebug - val target: String = s.targetValue + val target: String = s.targetValue.tap { value => + s.releaseValue.foreach { release => + if (value.toInt < release.toInt) + directBackendReporting.warning(NoPosition, + s"target platform version $value is older than the release version $release") + } + } private val singleOutDir = s.outputDirs.getSingleOutput // the call to `outputDirFor` should be frontendSynch'd, but we assume that the setting is not mutated during the backend From 17df71cbe5e606d3102d164cc4854e9fc86b7be8 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 29 Mar 2023 01:25:08 -0700 Subject: [PATCH 220/261] Regression test for no value discard warning --- test/files/neg/value-discard.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/files/neg/value-discard.scala b/test/files/neg/value-discard.scala index eed8a29a4d12..a6bde54878b3 100644 --- a/test/files/neg/value-discard.scala +++ b/test/files/neg/value-discard.scala @@ -18,4 +18,10 @@ final class UnusedTest { "": Unit // nowarn s.subtractOne("") // nowarn } + + def f(implicit x: Int): Boolean = x % 2 == 1 + + implicit def i: Int = 42 + + def u: Unit = f: Unit // nowarn } From 449293d0abb9d0f494b1ccc777fba0c8eb7aad50 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 30 Mar 2023 23:17:04 +0200 Subject: [PATCH 221/261] silence warning on JDK 20 as uncovered by recent merge of #10311 --- test/junit/scala/util/UsingTest.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/test/junit/scala/util/UsingTest.scala b/test/junit/scala/util/UsingTest.scala index cad0e00ade12..eb7350159cdc 100644 --- a/test/junit/scala/util/UsingTest.scala +++ b/test/junit/scala/util/UsingTest.scala @@ -745,6 +745,7 @@ class UsingTest { } } +@deprecated("ThreadDeath is deprecated on JDK 20", "") object UsingTest { final class ClosingVMError(message: String) extends VirtualMachineError(message) final class UsingVMError(message: String) extends VirtualMachineError(message) From 2a351ca9b0ce02f6bebdb180bc5e5f911a02ad1a Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 30 Mar 2023 16:45:45 +0200 Subject: [PATCH 222/261] Use BTypes in StringConcatFactory code gen This makes sure the BType for the inner class `MethodHandles$Lookup` is initialized (by getting the information from the Symbol) when emitting the InnerClass attribute. If not, the backend attempts to read the BType from the classfile, which can fail (newer than supported classfile version). --- .../scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala | 6 ++++-- src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index a2b2a21b365c..b56711f8d7ac 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -236,9 +236,11 @@ abstract class BCodeIdiomatic { asm.Type.getMethodDescriptor(StringRef.toASMType, argTypes:_*), new asm.Handle( asm.Opcodes.H_INVOKESTATIC, - "java/lang/invoke/StringConcatFactory", + jliStringConcatFactoryRef.internalName, "makeConcatWithConstants", - "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;", + List(jliMethodHandlesLookupRef, StringRef, jliMethodTypeRef, StringRef, ArrayBType(ObjectRef)) + .map(_.descriptor) + .mkString("(", "", s")${jliCallSiteRef.descriptor}"), false ), (recipe +: constants):_* diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 7f337b087bb2..98d293f855c9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -210,6 +210,9 @@ abstract class CoreBTypesFromSymbols[G <: Global] extends CoreBTypes { def jliLambdaMetafactoryRef : ClassBType = _jliLambdaMetafactoryRef.get private[this] lazy val _jliLambdaMetafactoryRef : LazyVar[ClassBType] = runLazy(classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])) + def jliStringConcatFactoryRef : ClassBType = _jliStringConcatFactoryRef.get + private[this] lazy val _jliStringConcatFactoryRef : LazyVar[ClassBType] = runLazy(classBTypeFromSymbol(getRequiredClass("java.lang.invoke.StringConcatFactory"))) + def srBoxesRunTimeRef : ClassBType = _srBoxesRunTimeRef.get private[this] lazy val _srBoxesRunTimeRef : LazyVar[ClassBType] = runLazy(classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])) From 67fc7700a6a050ccea158f8f4ab0654fa9e8aa63 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 4 Apr 2023 18:03:06 +0200 Subject: [PATCH 223/261] Upgrade to asm 9.5, for JDK21 support --- project/ScalaOptionParser.scala | 2 +- .../backend/jvm/analysis/BackendUtils.scala | 1 + .../nsc/settings/StandardScalaSettings.scala | 2 +- src/intellij/scala.ipr.SAMPLE | 26 +++++++++---------- .../scala/tools/nsc/settings/TargetTest.scala | 5 +++- versions.properties | 2 +- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 2a4cab358f87..abe611f3da08 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -126,5 +126,5 @@ object ScalaOptionParser { private def scaladocPathSettingNames = List("-doc-root-content", "-diagrams-dot-path") private def scaladocMultiStringSettingNames = List("-doc-external-doc") - private val targetSettingNames = (5 to 20).flatMap(v => s"$v" :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList + private val targetSettingNames = (5 to 21).flatMap(v => s"$v" :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index 9c12706f4b0f..06241205c971 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -90,6 +90,7 @@ abstract class BackendUtils extends PerRunInit { case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 case "20" => asm.Opcodes.V20 + case "21" => asm.Opcodes.V21 // to be continued... }) diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 01666cb2caf1..c3dd4064680b 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -115,7 +115,7 @@ object StandardScalaSettings { val MaxTargetVersion = ScalaVersion(javaSpecVersion) match { case SpecificScalaVersion(1, minor, _, _) => minor case SpecificScalaVersion(major, _, _, _) => major - case _ => 20 + case _ => 21 } val MaxSupportedTargetVersion = 8 val DefaultTargetVersion = "8" diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index 7636da684a44..698f08c12bca 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -231,7 +231,7 @@ - + @@ -250,7 +250,7 @@ - + @@ -262,7 +262,7 @@ - + @@ -280,7 +280,7 @@ - + @@ -290,7 +290,7 @@ - + @@ -317,7 +317,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -340,7 +340,7 @@ - + @@ -350,7 +350,7 @@ - + @@ -503,7 +503,7 @@ - + @@ -516,7 +516,7 @@ - + @@ -527,7 +527,7 @@ - + @@ -552,7 +552,7 @@ - + diff --git a/test/junit/scala/tools/nsc/settings/TargetTest.scala b/test/junit/scala/tools/nsc/settings/TargetTest.scala index a599fa9fd2d1..d81b4b1a71ca 100644 --- a/test/junit/scala/tools/nsc/settings/TargetTest.scala +++ b/test/junit/scala/tools/nsc/settings/TargetTest.scala @@ -98,7 +98,10 @@ class TargetTest { check("-target:jvm-20", "8", "20") check("-target:20", "8", "20") - checkFail("-target:jvm-21") // not yet... + check("-target:jvm-21", "8", "21") + check("-target:21", "8", "21") + + checkFail("-target:jvm-22") // not yet... checkFail("-target:jvm-3000") // not in our lifetime checkFail("-target:msil") // really? diff --git a/versions.properties b/versions.properties index 4f3b87bec6ed..13334c1494fd 100644 --- a/versions.properties +++ b/versions.properties @@ -21,5 +21,5 @@ scala.binary.version=2.12 scala-xml.version.number=2.1.0 scala-parser-combinators.version.number=1.0.7 scala-swing.version.number=2.0.3 -scala-asm.version=9.4.0-scala-1 +scala-asm.version=9.5.0-scala-1 jline.version=2.14.6 From dc2dc3495558a8dfc18170718f46c0b03164d61e Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 4 Apr 2023 18:08:23 +0200 Subject: [PATCH 224/261] Upgrade to asm 9.5, for JDK21 support --- project/ScalaOptionParser.scala | 2 +- .../backend/jvm/analysis/BackendUtils.scala | 1 + .../nsc/settings/StandardScalaSettings.scala | 2 +- src/intellij/scala.ipr.SAMPLE | 28 +++++++++---------- versions.properties | 2 +- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index a9954f61e738..aaa769ddbea6 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -140,5 +140,5 @@ object ScalaOptionParser { private def scaladocPathSettingNames = List("-doc-root-content", "-diagrams-dot-path") private def scaladocMultiStringSettingNames = List("-doc-external-doc") - private val targetSettingNames = (8 to 20).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList + private val targetSettingNames = (8 to 21).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index a73f1708efcd..75af78a8fc58 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -84,6 +84,7 @@ abstract class BackendUtils extends PerRunInit { case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 case "20" => asm.Opcodes.V20 + case "21" => asm.Opcodes.V21 // to be continued... }) diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 390ccec69062..4b798e0eb52d 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -103,7 +103,7 @@ object StandardScalaSettings { val MaxTargetVersion = ScalaVersion(javaSpecVersion) match { case SpecificScalaVersion(1, minor, _, _) => minor case SpecificScalaVersion(major, _, _, _) => major - case _ => 20 + case _ => 21 } private val AllTargetVersions = (MinTargetVersion to MaxTargetVersion).map(_.toString).to(List) diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index 85c4cef274f1..750a66fa890b 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -232,7 +232,7 @@ - + @@ -243,7 +243,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -266,7 +266,7 @@ - + @@ -287,7 +287,7 @@ - + @@ -296,14 +296,14 @@ - + - + @@ -312,7 +312,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -457,7 +457,7 @@ - + @@ -467,7 +467,7 @@ - + @@ -498,7 +498,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -525,7 +525,7 @@ - + diff --git a/versions.properties b/versions.properties index 9bd91efa4226..08aa27aa766c 100644 --- a/versions.properties +++ b/versions.properties @@ -6,7 +6,7 @@ starr.version=2.13.11-M1 # - scala-compiler: jline (% "optional") # Other usages: # - scala-asm: jar content included in scala-compiler -scala-asm.version=9.4.0-scala-1 +scala-asm.version=9.5.0-scala-1 # jna.version must be updated together with jline-terminal-jna jline.version=3.22.0 From 8d5e4b8a92197faf3949a2338ed3291746c9b74e Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 5 Apr 2023 12:36:31 +0200 Subject: [PATCH 225/261] GitHub Actions config: JDK 20 is GA --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82fdcdd0877e..052325d9192a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - java: [8, 11, 17, 20-ea] + java: [8, 11, 17, 20] runs-on: ${{matrix.os}} steps: - run: git config --global core.autocrlf false From d4f218105cecc8be3f4b26c46c5a431062eba468 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 8 Apr 2023 06:24:22 -0700 Subject: [PATCH 226/261] Forward port widened int div warning --- src/compiler/scala/tools/nsc/Reporting.scala | 1 + .../scala/tools/nsc/settings/Warnings.scala | 2 ++ .../scala/tools/nsc/typechecker/Typers.scala | 34 +++++++++++++------ .../scala/reflect/internal/Definitions.scala | 2 ++ .../reflect/runtime/JavaUniverseForce.scala | 1 + test/files/neg/lint-int-div-to-float.check | 18 ++++++++++ test/files/neg/lint-int-div-to-float.scala | 18 ++++++++++ 7 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 test/files/neg/lint-int-div-to-float.check create mode 100644 test/files/neg/lint-int-div-to-float.scala diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index 109ed4fa1a56..0bfac1753598 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -408,6 +408,7 @@ object Reporting { object LintUnitSpecialization extends Lint; add(LintUnitSpecialization) object LintMultiargInfix extends Lint; add(LintMultiargInfix) object LintPerformance extends Lint; add(LintPerformance) + object LintIntDivToFloat extends Lint; add(LintIntDivToFloat) sealed trait Feature extends WarningCategory { override def summaryCategory: WarningCategory = Feature } object Feature extends Feature { override def includes(o: WarningCategory): Boolean = o.isInstanceOf[Feature] }; add(Feature) diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 94f9a49c9604..a4553644adc5 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -214,6 +214,7 @@ trait Warnings { val ImplicitRecursion = LintWarning("implicit-recursion", "Implicit resolves to an enclosing definition.") val UniversalMethods = LintWarning("universal-methods", "Require arg to is/asInstanceOf.") val ArgDiscard = LintWarning("arg-discard", "-Wvalue-discard for adapted arguments.") + val IntDivToFloat = LintWarning("int-div-to-float", "Warn when an integer division is converted (widened) to floating point: `(someInt / 2): Double`.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -248,6 +249,7 @@ trait Warnings { def lintImplicitRecursion = lint.contains(ImplicitRecursion) || (warnSelfImplicit.value: @nowarn("cat=deprecation")) def lintUniversalMethods = lint.contains(UniversalMethods) def lintArgDiscard = lint.contains(ArgDiscard) + def lintIntDivToFloat = lint.contains(IntDivToFloat) // The Xlint warning group. val lint = MultiChoiceSetting( diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6f97a3ae760a..cdec1bc9537f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1124,17 +1124,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper @inline def warnValueDiscard(): Unit = if (!isPastTyper && settings.warnValueDiscard.value && !treeInfo.isThisTypeResult(tree) && !treeInfo.hasExplicitUnit(tree)) context.warning(tree.pos, s"discarded non-Unit value of type ${tree.tpe}", WarningCategory.WFlagValueDiscard) - @inline def warnNumericWiden(tpSym: Symbol, ptSym: Symbol): Unit = - if (!isPastTyper) { - val isInharmonic = ( - (tpSym == IntClass && ptSym == FloatClass) - || (tpSym == LongClass && (ptSym == FloatClass || ptSym == DoubleClass)) - ) - if (isInharmonic) - // not `context.deprecationWarning` because they are not buffered in silent mode - context.warning(tree.pos, s"Widening conversion from ${tpSym.name} to ${ptSym.name} is deprecated because it loses precision. Write `.to${ptSym.name}` instead.", WarningCategory.Deprecation) - else if (settings.warnNumericWiden.value) context.warning(tree.pos, "implicit numeric widening", WarningCategory.WFlagNumericWiden) + @inline def warnNumericWiden(tpSym: Symbol, ptSym: Symbol): Unit = if (!isPastTyper) { + val targetIsWide = ptSym == FloatClass || ptSym == DoubleClass + val isInharmonic = { + def intWidened = tpSym == IntClass && ptSym == FloatClass + def longWidened = tpSym == LongClass && targetIsWide + intWidened || longWidened } + if (isInharmonic) + // not `context.deprecationWarning` because they are not buffered in silent mode + context.warning(tree.pos, s"Widening conversion from ${tpSym.name} to ${ptSym.name} is deprecated because it loses precision. Write `.to${ptSym.name}` instead.", WarningCategory.Deprecation) + else { + object warnIntDiv extends Traverser { + def isInt(t: Tree) = ScalaIntegralValueClasses(t.tpe.typeSymbol) + override def traverse(tree: Tree): Unit = tree match { + case Apply(Select(q, nme.DIV), _) if isInt(q) => + context.warning(tree.pos, s"integral division is implicitly converted (widened) to floating point. Add an explicit `.to${ptSym.name}`.", WarningCategory.LintIntDivToFloat) + case Apply(Select(a1, _), List(a2)) if isInt(tree) && isInt(a1) && isInt(a2) => traverse(a1); traverse(a2) + case Select(q, _) if isInt(tree) && isInt(q) => traverse(q) + case _ => + } + } + if (targetIsWide && settings.lintIntDivToFloat) warnIntDiv(tree) + if (settings.warnNumericWiden.value) context.warning(tree.pos, "implicit numeric widening", WarningCategory.WFlagNumericWiden) + } + } // The <: Any requirement inhibits attempts to adapt continuation types to non-continuation types. val anyTyped = tree.tpe <:< AnyTpe diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 283f0f223fc5..2854d39c8049 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -182,6 +182,8 @@ trait Definitions extends api.StandardDefinitions { } def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses + lazy val ScalaIntegralValueClasses: Set[Symbol] = Set(CharClass, ByteClass, ShortClass, IntClass, LongClass) + def underlyingOfValueClass(clazz: Symbol): Type = clazz.derivedValueClassUnbox.tpe.resultType diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index bebba5e10782..1ce5bceab027 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -530,6 +530,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ScalaValueClasses definitions.ScalaValueClassesSet definitions.ScalaNumericValueClassesSet + definitions.ScalaIntegralValueClasses uncurry.VarargsSymbolAttachment uncurry.DesugaredParameterType diff --git a/test/files/neg/lint-int-div-to-float.check b/test/files/neg/lint-int-div-to-float.check new file mode 100644 index 000000000000..462a51ced0e9 --- /dev/null +++ b/test/files/neg/lint-int-div-to-float.check @@ -0,0 +1,18 @@ +lint-int-div-to-float.scala:6: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w1: Double = f / 2 + ^ +lint-int-div-to-float.scala:7: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w2: Double = (f / 2) * 3 + ^ +lint-int-div-to-float.scala:8: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w3: Double = -(f / 2) + ^ +lint-int-div-to-float.scala:9: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w4: Double = (new C).f / (new C).f * 3 + ^ +lint-int-div-to-float.scala:10: warning: integral division is implicitly converted (widened) to floating point. Add an explicit `.toDouble`. + def w5: Double = f - f.abs / 2 + ^ +error: No warnings can be incurred under -Werror. +5 warnings +1 error diff --git a/test/files/neg/lint-int-div-to-float.scala b/test/files/neg/lint-int-div-to-float.scala new file mode 100644 index 000000000000..4f66c481384e --- /dev/null +++ b/test/files/neg/lint-int-div-to-float.scala @@ -0,0 +1,18 @@ +// scalac: -Xlint -Xfatal-warnings + +class C { + def f = 1 + + def w1: Double = f / 2 + def w2: Double = (f / 2) * 3 + def w3: Double = -(f / 2) + def w4: Double = (new C).f / (new C).f * 3 + def w5: Double = f - f.abs / 2 + + def o1: Double = (f / 2).toDouble + def o2: Double = f.toDouble / 2 + def o3: Double = f / 2.toDouble + def o4: Double = f / 2d + def o5: Double = (new C).f.toDouble / (new C).f * 3 + def o6: Long = f / 2 + 3 // only warn if widening to a floating point, not when widening int to long +} From 6298da00727e516131fdef5a029b2fb68bf3e1ff Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 8 Apr 2023 08:51:01 -0700 Subject: [PATCH 227/261] Deprecate integral isWhole --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 1 - src/library/scala/math/BigInt.scala | 1 + src/library/scala/runtime/RichInt.scala | 1 + src/library/scala/runtime/ScalaNumberProxy.scala | 1 + test/files/run/is-valid-num.scala | 11 +++++------ test/scalacheck/scala/math/BigIntProperties.scala | 3 ++- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cdec1bc9537f..59cec79257f0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -837,7 +837,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // refuses to re-attempt typechecking, and presumes that someone // else was responsible for issuing the related type error! fun.setSymbol(NoSymbol) - case _ => } debuglog(s"fallback on implicits: $tree/$resetTree") // scala/bug#10066 Need to patch the enclosing tree in the context to make translation of Dynamic diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index c48bab4445ee..f4f248debde2 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -278,6 +278,7 @@ final class BigInt private (private var _bigInteger: BigInteger, private val _lo (shifted.signum != 0) && !(shifted equals BigInt.minusOne) } + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole: Boolean = true def underlying: BigInteger = bigInteger diff --git a/src/library/scala/runtime/RichInt.scala b/src/library/scala/runtime/RichInt.scala index 5538ba700ff1..fbe9aecd1b70 100644 --- a/src/library/scala/runtime/RichInt.scala +++ b/src/library/scala/runtime/RichInt.scala @@ -31,6 +31,7 @@ final class RichInt(val self: Int) extends AnyVal with ScalaNumberProxy[Int] wit /** Returns `'''true'''` if this number has no decimal component. * Always `'''true'''` for `RichInt`. */ + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole = true override def isValidInt = true diff --git a/src/library/scala/runtime/ScalaNumberProxy.scala b/src/library/scala/runtime/ScalaNumberProxy.scala index f5afdf0225ec..cf3e21460ca7 100644 --- a/src/library/scala/runtime/ScalaNumberProxy.scala +++ b/src/library/scala/runtime/ScalaNumberProxy.scala @@ -50,6 +50,7 @@ trait ScalaNumberProxy[T] extends Any with ScalaNumericAnyConversions with Typed @deprecated("use `sign` method instead", since = "2.13.0") def signum: Int = num.signum(self) } trait ScalaWholeNumberProxy[T] extends Any with ScalaNumberProxy[T] { + @deprecated("isWhole on an integer type is always true", "2.12.15") def isWhole = true } trait IntegralProxy[T] extends Any with ScalaWholeNumberProxy[T] with RangedProxy[T] { diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index eb364e055580..ef2388f8baa3 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -1,6 +1,5 @@ -/* - * filter: inliner warnings; re-run with - */ +// scalac: -Xlint -Werror +@annotation.nowarn("cat=deprecation&msg=isWhole") object Test { def x = BigInt("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") def y = BigDecimal("" + (Short.MaxValue + 1) + ".0") @@ -11,7 +10,7 @@ object Test { def l2 = Int.MinValue.toLong - 1 def main(args: Array[String]): Unit = { -// assert(x.isWhole, x) + assert(x.isWhole, x) assert(!x.isValidDouble, x) assert(!x.isValidFloat, x) assert(!x.isValidLong, x) @@ -171,7 +170,7 @@ object Test { if (!d.isInfinity) { val bd = BigDecimal(new java.math.BigDecimal(d)) -// assert(!bd.isWhole, bd) + assert(!bd.isWhole, bd) assert(bd.isExactDouble, bd) assert(bd.isExactFloat == isFloat, bd) assert(!bd.isValidLong, bd) @@ -221,7 +220,7 @@ object Test { assert(bd.isValidShort == isShort, bd) assert(bd.isValidByte == isByte, bd) -// assert(bi.isWhole, bi) + assert(bi.isWhole, bi) assert(bi.isValidDouble == isDouble, bi) assert(bi.isValidFloat == isFloat, bi) assert(bi.isValidLong == isLong, bi) diff --git a/test/scalacheck/scala/math/BigIntProperties.scala b/test/scalacheck/scala/math/BigIntProperties.scala index d036719b368f..a5107c4bf425 100644 --- a/test/scalacheck/scala/math/BigIntProperties.scala +++ b/test/scalacheck/scala/math/BigIntProperties.scala @@ -5,6 +5,7 @@ import Arbitrary.arbitrary import org.scalacheck.Prop._ import java.math.BigInteger import java.lang.Character +import scala.annotation.nowarn import scala.util.Random object BigIntProperties extends Properties("BigInt") { @@ -95,7 +96,7 @@ object BigIntProperties extends Properties("BigInt") { property("isValidLong") = forAll { (l: Long) => BigInt(l).isValidLong } property("isValidLong") = !BigInt("9223372036854775808").isValidLong property("isValidLong") = !BigInt("-9223372036854775809").isValidLong - property("isWhole") = forAll { (bi: BigInt) => bi.isWhole } + property("isWhole") = forAll { (bi: BigInt) => bi.isWhole: @nowarn } property("underlying") = forAll(bigInteger) { bi => BigInt(bi).underlying ?= bi } property("equals") = forAll(bigInteger, bigInteger) { (x, y) => (x == y) ?= (BigInt(x) equals BigInt(y)) } property("compare") = forAll(bigInteger, bigInteger) { (x, y) => x.compareTo(y) ?= BigInt(x).compare(y) } From 7f48bf375dc42c2a691697bc55b6dec6eae81787 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Sun, 9 Apr 2023 19:36:48 +0000 Subject: [PATCH 228/261] Update sbt-mima-plugin to 1.1.2 in 2.12.x --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index b45494705a5d..13706fde8fea 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -18,7 +18,7 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.1") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.2") libraryDependencies ++= Seq( "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.9.201909030838-r", From b0212d63d0d02e7a677ed5546f682424950ce30a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 8 Apr 2023 17:34:44 -0700 Subject: [PATCH 229/261] Lint select from Unit, or Int that incurs widening --- src/compiler/scala/tools/nsc/Reporting.scala | 7 +- .../scala/tools/nsc/settings/Warnings.scala | 4 +- .../scala/tools/nsc/typechecker/Typers.scala | 17 +- test/files/neg/t12728.check | 162 ++++++++++++++++++ test/files/neg/t12728.scala | 55 ++++++ test/junit/scala/ArrayTest.scala | 4 +- 6 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 test/files/neg/t12728.check create mode 100644 test/files/neg/t12728.scala diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index 0bfac1753598..ca46d2010d50 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -341,7 +341,10 @@ object Reporting { private val insertDash = "(?=[A-Z][a-z])".r val all: mutable.Map[String, WarningCategory] = mutable.Map.empty - private def add(c: WarningCategory): Unit = all += ((c.name, c)) + private def add(c: WarningCategory): Unit = { + require(!all.contains(c.name), s"lint '${c.name}' added twice") + all addOne (c.name, c) + } object Deprecation extends WarningCategory; add(Deprecation) @@ -409,6 +412,8 @@ object Reporting { object LintMultiargInfix extends Lint; add(LintMultiargInfix) object LintPerformance extends Lint; add(LintPerformance) object LintIntDivToFloat extends Lint; add(LintIntDivToFloat) + object LintUniversalMethods extends Lint; add(LintUniversalMethods) + object LintNumericMethods extends Lint; add(LintNumericMethods) sealed trait Feature extends WarningCategory { override def summaryCategory: WarningCategory = Feature } object Feature extends Feature { override def includes(o: WarningCategory): Boolean = o.isInstanceOf[Feature] }; add(Feature) diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index a4553644adc5..1b2413a7d4c2 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -212,7 +212,8 @@ trait Warnings { val UnitSpecialization = LintWarning("unit-special", "Warn for specialization of Unit in parameter position.") val MultiargInfix = LintWarning("multiarg-infix", "Infix operator was defined or used with multiarg operand.") val ImplicitRecursion = LintWarning("implicit-recursion", "Implicit resolves to an enclosing definition.") - val UniversalMethods = LintWarning("universal-methods", "Require arg to is/asInstanceOf.") + val UniversalMethods = LintWarning("universal-methods", "Require arg to is/asInstanceOf. No Unit receiver.") + val NumericMethods = LintWarning("numeric-methods", "Dubious usages, such as `42.isNaN`.") val ArgDiscard = LintWarning("arg-discard", "-Wvalue-discard for adapted arguments.") val IntDivToFloat = LintWarning("int-div-to-float", "Warn when an integer division is converted (widened) to floating point: `(someInt / 2): Double`.") @@ -248,6 +249,7 @@ trait Warnings { def multiargInfix = lint contains MultiargInfix def lintImplicitRecursion = lint.contains(ImplicitRecursion) || (warnSelfImplicit.value: @nowarn("cat=deprecation")) def lintUniversalMethods = lint.contains(UniversalMethods) + def lintNumericMethods = lint.contains(NumericMethods) def lintArgDiscard = lint.contains(ArgDiscard) def lintIntDivToFloat = lint.contains(IntDivToFloat) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 59cec79257f0..472fdc03cb31 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1265,7 +1265,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper adaptAfterOverloadResolution(tree, mode, pt, original) case NullaryMethodType(restpe) => // (2) if (hasUndets && settings.lintUniversalMethods && (isCastSymbol(tree.symbol) || isTypeTestSymbol(tree.symbol)) && context.undetparams.exists(_.owner == tree.symbol)) - context.warning(tree.pos, s"missing type argument to ${tree.symbol}", WarningCategory.LintAdaptedArgs) + context.warning(tree.pos, s"missing type argument to ${tree.symbol}", WarningCategory.LintUniversalMethods) val resTpDeconst = // keep constant types when they are safe to fold. erasure eliminates constant types modulo some exceptions, so keep those. if (isBeforeErasure && tree.symbol.isAccessor && tree.symbol.hasFlag(STABLE) && treeInfo.isExprSafeToInline(tree)) restpe else restpe.deconst @@ -5292,6 +5292,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def asDynamicCall = mkInvoke(context, tree, qual, name) map { t => wrapErrors(t, _.typed1(t, mode, pt)) } + def checkDubiousUnitSelection(result: Tree): Unit = + if (!isPastTyper && isUniversalMember(result.symbol)) + context.warning(tree.pos, s"dubious usage of ${result.symbol} with unit value", WarningCategory.LintUniversalMethods) val sym = tree.symbol orElse member(qual.tpe, name) orElse inCompanionForJavaStatic(qual.tpe.typeSymbol, name) if ((sym eq NoSymbol) && name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) { @@ -5299,9 +5302,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an // xml member to StringContext, which in turn has an unapply[Seq] method) + def checkDubiousAdaptation(sel: Tree): Unit = if (!isPastTyper && settings.lintNumericMethods) { + def richlyFloat = Set("isNaN", "isInfinity", "isInfinite", "isFinite", "isPosInfinity", "isNegInfinity", "round", "ceil", "floor") + val dubious = ScalaIntegralValueClasses(qualTp.typeSymbol) && richlyFloat(name.decoded) && ( + sel.symbol.owner.eq(BoxedFloatClass) + || + sel.symbol.owner.eq(rootMirror.getClassIfDefined("scala.runtime.RichFloat")) + ) + if (dubious) + context.warning(tree.pos, s"dubious usage of ${sel.symbol} with integer value", WarningCategory.LintNumericMethods) + } val qual1 = adaptToMemberWithArgs(tree, qual, name, mode) if ((qual1 ne qual) && !qual1.isErrorTyped) return typed(treeCopy.Select(tree, qual1, name), mode, pt) + .tap(checkDubiousAdaptation) } // This special-case complements the logic in `adaptMember` in erasure, it handles selections @@ -5419,6 +5433,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper name ) case _ => + if (settings.lintUniversalMethods && qualTp.widen.eq(UnitTpe)) checkDubiousUnitSelection(result) result } } diff --git a/test/files/neg/t12728.check b/test/files/neg/t12728.check new file mode 100644 index 000000000000..482707b6200a --- /dev/null +++ b/test/files/neg/t12728.check @@ -0,0 +1,162 @@ +t12728.scala:10: warning: dubious usage of method != with unit value + println(u.!=(x)) + ^ +t12728.scala:11: warning: dubious usage of method ## with unit value + println(u.##) + ^ +t12728.scala:12: warning: dubious usage of method == with unit value + println(u.==(x)) + ^ +t12728.scala:13: warning: dubious usage of method asInstanceOf with unit value + println(u.asInstanceOf[Any]) + ^ +t12728.scala:14: warning: dubious usage of method equals with unit value + println(u.equals(x)) + ^ +t12728.scala:15: warning: dubious usage of method hashCode with unit value + println(u.hashCode) + ^ +t12728.scala:16: warning: dubious usage of method isInstanceOf with unit value + println(u.isInstanceOf[Any]) + ^ +t12728.scala:17: warning: dubious usage of method toString with unit value + println(u.toString) + ^ +t12728.scala:20: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isNaN) + ^ +t12728.scala:20: warning: dubious usage of method isNaN with integer value + println(i.isNaN) + ^ +t12728.scala:21: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isInfinity) + ^ +t12728.scala:21: warning: dubious usage of method isInfinity with integer value + println(i.isInfinity) + ^ +t12728.scala:22: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isInfinite) + ^ +t12728.scala:22: warning: dubious usage of method isInfinite with integer value + println(i.isInfinite) + ^ +t12728.scala:23: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isFinite) + ^ +t12728.scala:23: warning: dubious usage of method isFinite with integer value + println(i.isFinite) + ^ +t12728.scala:24: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isPosInfinity) + ^ +t12728.scala:24: warning: dubious usage of method isPosInfinity with integer value + println(i.isPosInfinity) + ^ +t12728.scala:25: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.isNegInfinity) + ^ +t12728.scala:25: warning: dubious usage of method isNegInfinity with integer value + println(i.isNegInfinity) + ^ +t12728.scala:27: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.ceil) + ^ +t12728.scala:27: warning: dubious usage of method ceil with integer value + println(i.ceil) + ^ +t12728.scala:28: warning: Widening conversion from Int to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(i.floor) + ^ +t12728.scala:28: warning: dubious usage of method floor with integer value + println(i.floor) + ^ +t12728.scala:30: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isNaN) + ^ +t12728.scala:30: warning: dubious usage of method isNaN with integer value + println(l.isNaN) + ^ +t12728.scala:31: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isInfinity) + ^ +t12728.scala:31: warning: dubious usage of method isInfinity with integer value + println(l.isInfinity) + ^ +t12728.scala:32: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isInfinite) + ^ +t12728.scala:32: warning: dubious usage of method isInfinite with integer value + println(l.isInfinite) + ^ +t12728.scala:33: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isFinite) + ^ +t12728.scala:33: warning: dubious usage of method isFinite with integer value + println(l.isFinite) + ^ +t12728.scala:34: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isPosInfinity) + ^ +t12728.scala:34: warning: dubious usage of method isPosInfinity with integer value + println(l.isPosInfinity) + ^ +t12728.scala:35: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.isNegInfinity) + ^ +t12728.scala:35: warning: dubious usage of method isNegInfinity with integer value + println(l.isNegInfinity) + ^ +t12728.scala:37: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.ceil) + ^ +t12728.scala:37: warning: dubious usage of method ceil with integer value + println(l.ceil) + ^ +t12728.scala:38: warning: Widening conversion from Long to Float is deprecated because it loses precision. Write `.toFloat` instead. + println(l.floor) + ^ +t12728.scala:38: warning: dubious usage of method floor with integer value + println(l.floor) + ^ +t12728.scala:40: warning: dubious usage of method isNaN with integer value + println(c.isNaN) + ^ +t12728.scala:41: warning: dubious usage of method isInfinity with integer value + println(c.isInfinity) + ^ +t12728.scala:42: warning: dubious usage of method isInfinite with integer value + println(c.isInfinite) + ^ +t12728.scala:43: warning: dubious usage of method isFinite with integer value + println(c.isFinite) + ^ +t12728.scala:44: warning: dubious usage of method isPosInfinity with integer value + println(c.isPosInfinity) + ^ +t12728.scala:45: warning: dubious usage of method isNegInfinity with integer value + println(c.isNegInfinity) + ^ +t12728.scala:47: warning: dubious usage of method ceil with integer value + println(c.ceil) + ^ +t12728.scala:48: warning: dubious usage of method floor with integer value + println(c.floor) + ^ +t12728.scala:54: warning: dubious usage of method toString with unit value + def g = new java.lang.StringBuilder("hi").setCharAt(0, 'H').toString // "()" + ^ +t12728.scala:14: warning: comparing values of types Unit and Any using `equals` unsafely bypasses cooperative equality; use `==` instead + println(u.equals(x)) + ^ +t12728.scala:26: warning: method round in class RichInt is deprecated (since 2.11.0): this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + println(i.round) + ^ +t12728.scala:36: warning: method round in class RichLong is deprecated (since 2.11.0): this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + println(l.round) + ^ +t12728.scala:46: warning: method round in class RichInt is deprecated (since 2.11.0): this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value? + println(c.round) + ^ +error: No warnings can be incurred under -Werror. +53 warnings +1 error diff --git a/test/files/neg/t12728.scala b/test/files/neg/t12728.scala new file mode 100644 index 000000000000..6ccb0f19d639 --- /dev/null +++ b/test/files/neg/t12728.scala @@ -0,0 +1,55 @@ +// scalac: -Werror -Xlint + +class C { + val u = () + val i = 42 + val l = 42L + val c = 'c' + val x = null: Any + + println(u.!=(x)) + println(u.##) + println(u.==(x)) + println(u.asInstanceOf[Any]) + println(u.equals(x)) + println(u.hashCode) + println(u.isInstanceOf[Any]) + println(u.toString) + println(i.toString) + + println(i.isNaN) + println(i.isInfinity) + println(i.isInfinite) + println(i.isFinite) + println(i.isPosInfinity) + println(i.isNegInfinity) + println(i.round) + println(i.ceil) + println(i.floor) + + println(l.isNaN) + println(l.isInfinity) + println(l.isInfinite) + println(l.isFinite) + println(l.isPosInfinity) + println(l.isNegInfinity) + println(l.round) + println(l.ceil) + println(l.floor) + + println(c.isNaN) + println(c.isInfinity) + println(c.isInfinite) + println(c.isFinite) + println(c.isPosInfinity) + println(c.isNegInfinity) + println(c.round) + println(c.ceil) + println(c.floor) +} + +class UseCase { + def f = new scala.StringBuilder("hi").setCharAt(0, 'H').toString // "Hi" + + def g = new java.lang.StringBuilder("hi").setCharAt(0, 'H').toString // "()" +} diff --git a/test/junit/scala/ArrayTest.scala b/test/junit/scala/ArrayTest.scala index ef0ff59f1bb5..dc80d0e4e1cf 100644 --- a/test/junit/scala/ArrayTest.scala +++ b/test/junit/scala/ArrayTest.scala @@ -4,11 +4,13 @@ import org.junit.Assert.{ assertArrayEquals, assertFalse, assertTrue } import org.junit.Test import scala.runtime.BoxedUnit +import scala.util.chaining._ class ArrayTest { @Test def testArrayCopyOfUnit(): Unit = { - val expected = new Array[BoxedUnit](32).asInstanceOf[Array[AnyRef]]; java.util.Arrays.fill(expected, ().asInstanceOf[AnyRef]) + val expected = new Array[BoxedUnit](32).asInstanceOf[Array[AnyRef]] + .tap(array => java.util.Arrays.fill(array, (): Any)) assertArrayEquals(expected, Array.copyOf(Array[Unit](), 32).asInstanceOf[Array[AnyRef]]) assertArrayEquals(expected, Array.copyAs[Unit](Array[Nothing](), 32).asInstanceOf[Array[AnyRef]]) assertArrayEquals(expected, Array.copyAs[Unit](Array[Unit](), 32).asInstanceOf[Array[AnyRef]]) From 21fc8284839070fe66e50898d4d80e2106958f29 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 9 Apr 2023 22:42:57 -0700 Subject: [PATCH 230/261] Simplify check for float conversion --- src/compiler/scala/tools/nsc/Reporting.scala | 5 +---- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 8 ++------ src/reflect/scala/reflect/internal/Definitions.scala | 1 + src/reflect/scala/reflect/runtime/JavaUniverseForce.scala | 1 + 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index ca46d2010d50..eadd5e9c4d8a 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -341,10 +341,7 @@ object Reporting { private val insertDash = "(?=[A-Z][a-z])".r val all: mutable.Map[String, WarningCategory] = mutable.Map.empty - private def add(c: WarningCategory): Unit = { - require(!all.contains(c.name), s"lint '${c.name}' added twice") - all addOne (c.name, c) - } + private def add(c: WarningCategory): Unit = all.put(c.name, c).ensuring(_.isEmpty, s"lint '${c.name}' added twice") object Deprecation extends WarningCategory; add(Deprecation) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 472fdc03cb31..8ac7eef53025 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5303,12 +5303,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // xml member to StringContext, which in turn has an unapply[Seq] method) def checkDubiousAdaptation(sel: Tree): Unit = if (!isPastTyper && settings.lintNumericMethods) { - def richlyFloat = Set("isNaN", "isInfinity", "isInfinite", "isFinite", "isPosInfinity", "isNegInfinity", "round", "ceil", "floor") - val dubious = ScalaIntegralValueClasses(qualTp.typeSymbol) && richlyFloat(name.decoded) && ( - sel.symbol.owner.eq(BoxedFloatClass) - || - sel.symbol.owner.eq(rootMirror.getClassIfDefined("scala.runtime.RichFloat")) - ) + val dubious = ScalaIntegralValueClasses(qualTp.typeSymbol) && ( + sel.symbol.owner.eq(BoxedFloatClass) || sel.symbol.owner.eq(RichFloatClass)) if (dubious) context.warning(tree.pos, s"dubious usage of ${sel.symbol} with integer value", WarningCategory.LintNumericMethods) } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 2854d39c8049..ac7c6353aa1a 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -104,6 +104,7 @@ trait Definitions extends api.StandardDefinitions { lazy val lazyHolders = symbolsMap(ScalaValueClasses, x => getClassIfDefined("scala.runtime.Lazy" + x)) lazy val LazyRefClass = getClassIfDefined("scala.runtime.LazyRef") lazy val LazyUnitClass = getClassIfDefined("scala.runtime.LazyUnit") + lazy val RichFloatClass = getClassIfDefined("scala.runtime.RichFloat") lazy val allRefClasses: Set[Symbol] = { refClass.values.toSet ++ volatileRefClass.values.toSet ++ Set(VolatileObjectRefClass, ObjectRefClass) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 1ce5bceab027..703e000cacd1 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -506,6 +506,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.lazyHolders definitions.LazyRefClass definitions.LazyUnitClass + definitions.RichFloatClass definitions.allRefClasses definitions.UnitClass definitions.ByteClass From 905c92f47270aa2957fd7f0d8e1266a6d392d869 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 11 Apr 2023 09:36:04 -0700 Subject: [PATCH 231/261] realeasy includes annoying tests --- .../tools/partest/nest/AbstractRunner.scala | 24 ++++++++++++------- .../scala/tools/partest/nest/Runner.scala | 3 ++- test/files/run/blank.scala | 11 +++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 test/files/run/blank.scala diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index 8a19b6e893a0..4212873beaf1 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -296,7 +296,8 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour val isRerun = config.optFailed val rerunTests = if (isRerun) testKinds.failedTests else Nil - def miscTests = individualTests ++ greppedTests ++ branchedTests ++ rerunTests + val specialTests = if (realeasy) List(Path("test/files/run/t6240-universe-code-gen.scala")) else Nil + def miscTests = List(individualTests, greppedTests, branchedTests, rerunTests, specialTests).flatten val givenKinds = standardKinds filter config.parsed.isSet val kinds = ( @@ -312,16 +313,21 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour def testContributors = { List( - if (rerunTests.isEmpty) "" else "previously failed tests", - if (kindsTests.isEmpty) "" else s"${kinds.size} named test categories", - if (greppedTests.isEmpty) "" else s"${greppedTests.size} tests matching '$grepExpr'", - if (branchedTests.isEmpty) "" else s"${branchedTests.size} tests modified on this branch", - if (individualTests.isEmpty) "" else "specified tests", - ).filterNot(_.isEmpty).mkString(", ") + (rerunTests, "previously failed tests"), + (kindsTests, s"${kinds.size} named test categories"), + (greppedTests, s"${greppedTests.size} tests matching '$grepExpr'"), + (branchedTests, s"${branchedTests.size} tests modified on this branch"), + (individualTests, "specified tests"), + (specialTests, "other tests you might have forgotten"), + ).filterNot(_._1.isEmpty).map(_._2) match { + case Nil => "the well of despair. I see you're not in a testing mood." + case one :: Nil => one + case all => all.init.mkString("", ", ", s", and ${all.last}") + } } - val allTests: Array[Path] = distinctBy(miscTests ++ kindsTests)(_.toCanonical).sortBy(_.toString).toArray - val grouped = (allTests groupBy kindOf).toArray sortBy (x => standardKinds indexOf x._1) + val allTests: Array[Path] = distinctBy(miscTests ::: kindsTests)(_.toCanonical).sortBy(_.toString).toArray + val grouped = allTests.groupBy(kindOf).toArray.sortBy(x => standardKinds.indexOf(x._1)) onlyIndividualTests = individualTests.nonEmpty && rerunTests.isEmpty && kindsTests.isEmpty && greppedTests.isEmpty totalTests = allTests.size diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 0ceafe355dde..a44cea9b3635 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -531,7 +531,8 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { else from.toInt <= currentJavaVersion && currentJavaVersion <= to.toInt else currentJavaVersion >= from.toInt - if (ok) None + if (ok && suiteRunner.realeasy && from.toInt > 8) Some(genSkip(s"skipped on Java $javaSpecVersion, compiling against JDK8 but must run on $v")) + else if (ok) None else Some(genSkip(s"skipped on Java $javaSpecVersion, only running on $v")) case v => Some(genFail(s"invalid javaVersion range in test comment: $v")) diff --git a/test/files/run/blank.scala b/test/files/run/blank.scala new file mode 100644 index 000000000000..0a96ee5f5b49 --- /dev/null +++ b/test/files/run/blank.scala @@ -0,0 +1,11 @@ +// javaVersion: 11+ +// +// skalac: --release:8 +// trivial manual test for partest --realeasy, which sets --release:8. +// under --realeasy, skip this test because of the javaVersion, irrespective of JDK in use. +// otherwise, this test passes trivially on JDK11+ and is skipped on lesser JDKs. +// note that explicit --release:8 asks to compile against JDK8 but only run on the requested version. + +object Test extends App { + assert("".isBlank) // String#isBlank was added in JDK11 +} From 8d45170c1d81ec64b79d2daff255de1b7718d102 Mon Sep 17 00:00:00 2001 From: Liang Yan Date: Wed, 12 Apr 2023 08:55:10 +0800 Subject: [PATCH 232/261] Update LinkedHash{Map,Set} from factory method Signed-off-by: Liang Yan --- .../scala/collection/mutable/LinkedHashMap.scala | 11 ++++++----- .../scala/collection/mutable/LinkedHashSet.scala | 16 +++++++++++----- .../collection/mutable/LinkedHashMapTest.scala | 14 ++++++++++++++ .../collection/mutable/LinkedHashSetTest.scala | 14 ++++++++++++++ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index 47c3c16f4163..bc663f1d37d8 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -478,11 +478,12 @@ object LinkedHashMap extends MapFactory[LinkedHashMap] { def empty[K, V] = new LinkedHashMap[K, V] - def from[K, V](it: collection.IterableOnce[(K, V)]) = - it match { - case lhm: LinkedHashMap[K, V] => lhm - case _ => Growable.from(empty[K, V], it) - } + def from[K, V](it: collection.IterableOnce[(K, V)]) = { + val newlhm = empty[K, V] + newlhm.sizeHint(it.knownSize) + newlhm.addAll(it) + newlhm + } def newBuilder[K, V] = new GrowableBuilder(empty[K, V]) diff --git a/src/library/scala/collection/mutable/LinkedHashSet.scala b/src/library/scala/collection/mutable/LinkedHashSet.scala index 684a80cbc978..0c01f8ea79ea 100644 --- a/src/library/scala/collection/mutable/LinkedHashSet.scala +++ b/src/library/scala/collection/mutable/LinkedHashSet.scala @@ -82,6 +82,11 @@ class LinkedHashSet[A] def contains(elem: A): Boolean = findEntry(elem) ne null + override def sizeHint(size: Int): Unit = { + val target = tableSizeFor(((size + 1).toDouble / LinkedHashSet.defaultLoadFactor).toInt) + if (target > table.length) growTable(target) + } + override def add(elem: A): Boolean = { if (contentSize + 1 >= threshold) growTable(table.length * 2) val hash = computeHash(elem) @@ -311,11 +316,12 @@ object LinkedHashSet extends IterableFactory[LinkedHashSet] { override def empty[A]: LinkedHashSet[A] = new LinkedHashSet[A] - def from[E](it: collection.IterableOnce[E]) = - it match { - case lhs: LinkedHashSet[E] => lhs - case _ => Growable.from(empty[E], it) - } + def from[E](it: collection.IterableOnce[E]) = { + val newlhs = empty[E] + newlhs.sizeHint(it.knownSize) + newlhs.addAll(it) + newlhs + } def newBuilder[A] = new GrowableBuilder(empty[A]) diff --git a/test/junit/scala/collection/mutable/LinkedHashMapTest.scala b/test/junit/scala/collection/mutable/LinkedHashMapTest.scala index 6fb26e88a5d6..6467bd299459 100644 --- a/test/junit/scala/collection/mutable/LinkedHashMapTest.scala +++ b/test/junit/scala/collection/mutable/LinkedHashMapTest.scala @@ -169,4 +169,18 @@ class LinkedHashMapTest { assertEquals(hashMapCount4, mutable.LinkedHashMap(countingKey1 -> "a")) } + + @Test + def testfromfactorymethod(): Unit = { + val data = List((1, 'a'), (2,'b'),(3,'c'), (4,'d'),(5,'e'),(6,'f')) + val lhm = new mutable.LinkedHashMap[Int, Char] + data.foreach(x => lhm.addOne(x)) + + val fromlhm1 = LinkedHashMap.from(data) + assertEquals(fromlhm1, lhm) + + val fromlhm2 = LinkedHashMap.from(lhm) + assertEquals(fromlhm2, lhm) + assertFalse(fromlhm2 eq lhm) + } } diff --git a/test/junit/scala/collection/mutable/LinkedHashSetTest.scala b/test/junit/scala/collection/mutable/LinkedHashSetTest.scala index 2ee4c2c14b33..8e812c4c6c4a 100644 --- a/test/junit/scala/collection/mutable/LinkedHashSetTest.scala +++ b/test/junit/scala/collection/mutable/LinkedHashSetTest.scala @@ -24,4 +24,18 @@ class LinkedHashSetTest { lhs.clear() Assert.assertNull(lhs.lastItemRef) } + + @Test + def testfromfactorymethod(): Unit = { + val data = List(1,2,3,4,5,6,7,8) + val lhs = new mutable.LinkedHashSet[Int] + data.foreach(x => lhs.addOne(x)) + + val fromlhs1 = LinkedHashSet.from(data) + Assert.assertEquals(fromlhs1, lhs) + + val fromlhs2 = LinkedHashSet.from(lhs) + Assert.assertEquals(fromlhs2, lhs) + Assert.assertFalse(fromlhs2 eq lhs) + } } From 2a7654708d0ddc3a21d755f1ad5a3cbdb8c5edef Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 4 Apr 2023 15:31:51 +0200 Subject: [PATCH 233/261] custom LinkedHashSet subclass for match analysis --- .../tools/nsc/transform/patmat/Logic.scala | 54 +++++++++++++------ .../nsc/transform/patmat/SolvingTest.scala | 3 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index ec2c739f0c62..b5ea1d2f91d1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -13,11 +13,11 @@ package scala package tools.nsc.transform.patmat -import scala.collection.mutable import scala.collection.immutable.ArraySeq -import scala.collection.IterableOps +import scala.collection.{IterableOps, mutable} import scala.reflect.internal.util.Collections._ import scala.reflect.internal.util.HashSet +import scala.tools.nsc.transform.patmat.Logic.LogicLinkedHashSet trait Logic extends Debugging { import global._ @@ -114,22 +114,21 @@ trait Logic extends Debugging { def implications: List[(Sym, List[Sym], List[Sym])] } - // The error message of t7020 assumes the ops are ordered implicitly - // However, ListSet is slow (concatenate cost?), so use - // scala.collection.mutable.LinkedHashSet (which grantees "the order in which elements were inserted into the set") + // Using LogicLinkedHashSet (a custom mutable.LinkedHashSet subclass) to ensure deterministic exhaustivity + // messages. immutable.ListSet was too slow (concatenate cost? scala/bug#12499). // would be nice to statically check whether a prop is equational or pure, // but that requires typing relations like And(x: Tx, y: Ty) : (if(Tx == PureProp && Ty == PureProp) PureProp else Prop) - final case class And(ops: mutable.LinkedHashSet[Prop]) extends Prop + final case class And(ops: LogicLinkedHashSet[Prop]) extends Prop object And { def apply(ps: Prop*) = create(ps) - def create(ps: Iterable[Prop]) = new And(ps.to(mutable.LinkedHashSet)) + def create(ps: Iterable[Prop]) = new And(ps.to(LogicLinkedHashSet)) } - final case class Or(ops: mutable.LinkedHashSet[Prop]) extends Prop + final case class Or(ops: LogicLinkedHashSet[Prop]) extends Prop object Or { def apply(ps: Prop*) = create(ps) - def create(ps: Iterable[Prop]) = new Or(ps.to(mutable.LinkedHashSet)) + def create(ps: Iterable[Prop]) = new Or(ps.to(LogicLinkedHashSet)) } final case class Not(a: Prop) extends Prop @@ -286,7 +285,7 @@ trait Logic extends Debugging { def simplifyAnd(ps: Iterable[Prop]): Prop = { // recurse for nested And (pulls all Ands up) // build up Set in order to remove duplicates - val props = mutable.LinkedHashSet.empty[Prop] + val props = LogicLinkedHashSet.empty[Prop] for (prop <- ps) { simplifyProp(prop) match { case True => // ignore `True` @@ -302,7 +301,7 @@ trait Logic extends Debugging { def simplifyOr(ps: Iterable[Prop]): Prop = { // recurse for nested Or (pulls all Ors up) // build up Set in order to remove duplicates - val props = mutable.LinkedHashSet.empty[Prop] + val props = LogicLinkedHashSet.empty[Prop] for (prop <- ps) { simplifyProp(prop) match { case False => // ignore `False` @@ -343,7 +342,7 @@ trait Logic extends Debugging { } def gatherVariables(p: Prop): collection.Set[Var] = { - val vars = new mutable.LinkedHashSet[Var]() + val vars = new LogicLinkedHashSet[Var]() (new PropTraverser { override def applyVar(v: Var) = vars += v })(p) @@ -351,7 +350,7 @@ trait Logic extends Debugging { } def gatherSymbols(p: Prop): collection.Set[Sym] = { - val syms = new mutable.LinkedHashSet[Sym]() + val syms = new LogicLinkedHashSet[Sym]() (new PropTraverser { override def applySymbol(s: Sym) = syms += s })(p) @@ -409,7 +408,7 @@ trait Logic extends Debugging { def removeVarEq(props: List[Prop], modelNull: Boolean = false): (Prop, List[Prop]) = { val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaVarEq) else null - val vars = new mutable.LinkedHashSet[Var] + val vars = new LogicLinkedHashSet[Var] object gatherEqualities extends PropTraverser { override def apply(p: Prop) = p match { @@ -516,6 +515,31 @@ trait Logic extends Debugging { } } +object Logic { + import scala.annotation.nowarn + import scala.collection.mutable.{Growable, GrowableBuilder, SetOps} + import scala.collection.{IterableFactory, IterableFactoryDefaults, StrictOptimizedIterableOps} + + // Local subclass because we can't override `addAll` in the collections (bin compat), see PR scala/scala#10361 + @nowarn("msg=inheritance from class LinkedHashSet") + class LogicLinkedHashSet[A] extends mutable.LinkedHashSet[A] + with SetOps[A, LogicLinkedHashSet, LogicLinkedHashSet[A]] + with StrictOptimizedIterableOps[A, LogicLinkedHashSet, LogicLinkedHashSet[A]] + with IterableFactoryDefaults[A, LogicLinkedHashSet] { + override def iterableFactory: IterableFactory[LogicLinkedHashSet] = LogicLinkedHashSet + override def addAll(xs: IterableOnce[A]): this.type = { + sizeHint(xs.knownSize) + super.addAll(xs) + } + } + + object LogicLinkedHashSet extends IterableFactory[LogicLinkedHashSet] { + override def from[A](source: IterableOnce[A]): LogicLinkedHashSet[A] = Growable.from(empty[A], source) + override def empty[A]: LogicLinkedHashSet[A] = new LogicLinkedHashSet[A] + override def newBuilder[A]: mutable.Builder[A, LogicLinkedHashSet[A]] = new GrowableBuilder(empty[A]) + } +} + trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { trait TreesAndTypesDomain extends PropositionalLogic with CheckableTreeAndTypeAnalysis { type Type = global.Type @@ -733,8 +757,8 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { } - import global.{ConstantType, SingletonType, Literal, Ident, singleType, TypeBounds, NoSymbol} import global.definitions._ + import global.{ConstantType, Ident, Literal, NoSymbol, SingletonType, TypeBounds, singleType} // all our variables range over types diff --git a/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala b/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala index 40643b2af887..fa7ce9091d96 100644 --- a/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala +++ b/test/junit/scala/tools/nsc/transform/patmat/SolvingTest.scala @@ -5,6 +5,7 @@ import org.junit.Test import scala.collection.mutable import scala.reflect.internal.util.Position +import scala.tools.nsc.transform.patmat.Logic.LogicLinkedHashSet import scala.tools.nsc.{Global, Settings} object TestSolver extends Logic with Solving { @@ -579,7 +580,7 @@ class SolvingTest { def pairWiseEncoding(ops: List[Sym]) = { And(ops.combinations(2).collect { case a :: b :: Nil => Or(Not(a), Not(b)): Prop - }.to(mutable.LinkedHashSet)) + }.to(LogicLinkedHashSet)) } @Test From 2360477d74739b3fe473a1db43fdceda93b12bb1 Mon Sep 17 00:00:00 2001 From: kenji yoshida <6b656e6a69@gmail.com> Date: Tue, 18 Apr 2023 15:18:12 +0900 Subject: [PATCH 234/261] fix `SimpleTracer` scaladoc --- src/compiler/scala/tools/nsc/util/SimpleTracer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index af49114e52f5..da9cccbcb923 100644 --- a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala +++ b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala @@ -18,8 +18,8 @@ package util import java.io.PrintStream /** A simple tracer - * @param out: The print stream where trace info should be sent - * @param enabled: A condition that must be true for trace info to be produced. + * @param out The print stream where trace info should be sent + * @param enabled A condition that must be true for trace info to be produced. */ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { def apply[T](msg: => String)(value: T): T = { From 5849a58460621277f9d63d18a33946f9fb783bca Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 5 Aug 2022 10:16:05 -0700 Subject: [PATCH 235/261] Honor PermittedSubclasses Accept (non-)sealed in Java sources. Defer setting sealed from source to follow-up PR, which will populate child classes lazily. --- src/compiler/scala/tools/nsc/Global.scala | 6 +- .../tools/nsc/ast/parser/CommonTokens.scala | 2 +- .../scala/tools/nsc/ast/parser/Tokens.scala | 1 - .../scala/tools/nsc/javac/JavaParsers.scala | 58 ++++++++++++--- .../scala/tools/nsc/javac/JavaTokens.scala | 1 + .../symtab/classfile/ClassfileParser.scala | 32 +++++++-- .../tools/nsc/typechecker/ContextErrors.scala | 2 +- .../scala/tools/nsc/typechecker/Namers.scala | 72 +++++++++++-------- .../scala/tools/nsc/typechecker/Typers.scala | 13 ++-- .../tools/partest/nest/AbstractRunner.scala | 2 +- .../reflect/internal/StdAttachments.scala | 4 ++ .../scala/reflect/internal/StdNames.scala | 12 +++- .../scala/reflect/internal/Symbols.scala | 16 ++--- .../scala/reflect/internal/Trees.scala | 2 +- .../scala/reflect/internal/Types.scala | 7 +- .../reflect/runtime/JavaUniverseForce.scala | 2 + test/files/neg/t12159.check | 4 ++ test/files/neg/t12159/H.java | 19 +++++ test/files/neg/t12159/I.java | 6 ++ test/files/neg/t12159/J.java | 6 ++ test/files/neg/t12159/M.java | 9 +++ test/files/neg/t12159/s.scala | 11 +++ test/files/neg/t12159b.check | 4 ++ test/files/neg/t12159b/I.java | 6 ++ test/files/neg/t12159b/J.java | 6 ++ test/files/neg/t12159b/s_2.scala | 5 ++ test/files/neg/t12159c.check | 7 ++ test/files/neg/t12159c/H.java | 14 ++++ test/files/neg/t12159c/s_2.scala | 12 ++++ test/files/pos/t12159/H.java | 19 +++++ test/files/pos/t12159/I.java | 6 ++ test/files/pos/t12159/J.java | 6 ++ test/files/pos/t12159/M.java | 9 +++ test/files/pos/t12159/s.scala | 6 ++ test/files/pos/t12159b/H_1.java | 14 ++++ test/files/pos/t12159b/s_2.scala | 13 ++++ test/files/pos/t12474/Nat.java | 6 ++ test/files/pos/t12474/s.scala | 5 ++ 38 files changed, 349 insertions(+), 76 deletions(-) create mode 100644 test/files/neg/t12159.check create mode 100644 test/files/neg/t12159/H.java create mode 100644 test/files/neg/t12159/I.java create mode 100644 test/files/neg/t12159/J.java create mode 100644 test/files/neg/t12159/M.java create mode 100644 test/files/neg/t12159/s.scala create mode 100644 test/files/neg/t12159b.check create mode 100644 test/files/neg/t12159b/I.java create mode 100644 test/files/neg/t12159b/J.java create mode 100644 test/files/neg/t12159b/s_2.scala create mode 100644 test/files/neg/t12159c.check create mode 100644 test/files/neg/t12159c/H.java create mode 100644 test/files/neg/t12159c/s_2.scala create mode 100644 test/files/pos/t12159/H.java create mode 100644 test/files/pos/t12159/I.java create mode 100644 test/files/pos/t12159/J.java create mode 100644 test/files/pos/t12159/M.java create mode 100644 test/files/pos/t12159/s.scala create mode 100644 test/files/pos/t12159b/H_1.java create mode 100644 test/files/pos/t12159b/s_2.scala create mode 100644 test/files/pos/t12474/Nat.java create mode 100644 test/files/pos/t12474/s.scala diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 89da5fe0ac59..d8575d20914e 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1727,10 +1727,8 @@ class Global(var currentSettings: Settings, reporter0: Reporter) } // class Run def printAllUnits(): Unit = { - print("[[syntax trees at end of %25s]]".format(phase)) - exitingPhase(phase)(currentRun.units foreach { unit => - nodePrinters showUnit unit - }) + print(f"[[syntax trees at end of $phase%25s]]") + exitingPhase(phase)(currentRun.units.foreach(nodePrinters.showUnit(_))) } /** We resolve the class/object ambiguity by passing a type/term name. diff --git a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala index 090c517054f7..a3b858c34fbc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala @@ -51,7 +51,7 @@ abstract class CommonTokens { // J: PUBLIC = 42 final val PROTECTED = 43 final val PRIVATE = 44 - // S: SEALED = 45 + final val SEALED = 45 // J: contextual keyword final val ABSTRACT = 46 // J: DEFAULT = 47 // J: STATIC = 48 diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index 56dbf3db7494..c846cc55ec81 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -28,7 +28,6 @@ object Tokens extends CommonTokens { /** modifiers */ final val IMPLICIT = 40 final val OVERRIDE = 41 - final val SEALED = 45 final val LAZY = 55 final val MACRO = 57 diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 2049693a81f3..c6894afba4df 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -16,14 +16,14 @@ package scala.tools.nsc package javac -import scala.collection.mutable.ListBuffer import symtab.Flags import JavaTokens._ -import scala.annotation.tailrec +import scala.annotation._ +import scala.collection.mutable.ListBuffer import scala.language.implicitConversions -import scala.reflect.internal.util.Position -import scala.reflect.internal.util.ListOfNil +import scala.reflect.internal.util.{ListOfNil, Position} import scala.tools.nsc.Reporting.WarningCategory +import scala.util.chaining._ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val global : Global @@ -493,11 +493,39 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { case SYNCHRONIZED => in.nextToken() case _ => - val privateWithin: TypeName = - if (isPackageAccess && !inInterface) thisPackageName - else tpnme.EMPTY - - return Modifiers(flags, privateWithin) withAnnotations annots + val unsealed = 0L // no flag for UNSEALED + def consume(added: FlagSet): false = { in.nextToken(); /*flags |= added;*/ false } + def lookingAhead(s: String): Boolean = { + import scala.reflect.internal.Chars._ + var i = 0 + val n = s.length + val lookahead = in.in.lookahead + while (i < n && lookahead.ch != SU) { + if (lookahead.ch != s.charAt(i)) return false + lookahead.next() + i += 1 + } + i == n && Character.isWhitespace(lookahead.ch) + } + val done = (in.token != IDENTIFIER) || ( + in.name match { + case nme.javaRestrictedIdentifiers.SEALED => consume(Flags.SEALED) + case nme.javaRestrictedIdentifiers.UNSEALED => consume(unsealed) + case nme.javaRestrictedIdentifiers.NON => + !lookingAhead("-sealed") || { + in.nextToken() + in.nextToken() + consume(unsealed) + } + case _ => true + } + ) + if (done) { + val privateWithin: TypeName = + if (isPackageAccess && !inInterface) thisPackageName + else tpnme.EMPTY + return Modifiers(flags, privateWithin) withAnnotations annots + } } } abort("should not be here") @@ -802,6 +830,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { List() } + def permitsOpt() = + if (in.token == IDENTIFIER && in.name == nme.javaRestrictedIdentifiers.PERMITS) { + in.nextToken() + repsep(() => typ(), COMMA) + } + else Nil + def classDecl(mods: Modifiers): List[Tree] = { accept(CLASS) val pos = in.currentPos @@ -815,9 +850,11 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { javaLangObject() } val interfaces = interfacesOpt() + val permits = permitsOpt() val (statics, body) = typeBody(CLASS) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body)) + .tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits))) }) } @@ -878,11 +915,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } else { List(javaLangObject()) } + val permits = permitsOpt() val (statics, body) = typeBody(INTERFACE) addCompanionObject(statics, atPos(pos) { ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT, name, tparams, makeTemplate(parents, body)) + .tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits))) }) } @@ -905,7 +944,6 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } else if (in.token == SEMI) { in.nextToken() } else { - // See "14.3. Local Class and Interface Declarations" adaptRecordIdentifier() if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) diff --git a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala index a124d1b90aaa..66fcdf7c069c 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala @@ -38,6 +38,7 @@ object JavaTokens extends ast.parser.CommonTokens { final val NATIVE = 53 final val STRICTFP = 54 final val THROWS = 56 + final val UNSEALED = 57 // contextual keyword /** templates */ final val INTERFACE = 66 diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 80d7285f9e56..c841725e004e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -853,10 +853,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { in.skip(attrLen) case tpnme.RuntimeAnnotationATTR => - val numAnnots = u2() + val numAnnots = u2() val annots = new ListBuffer[AnnotationInfo] - for (n <- 0 until numAnnots; annot <- parseAnnotation(u2())) - annots += annot + numAnnots times { + parseAnnotation(u2()).foreach(annots.addOne) + } /* `sym.withAnnotations(annots)`, like `sym.addAnnotation(annot)`, prepends, * so if we parsed in classfile order we would wind up with the annotations * in reverse order in `sym.annotations`. Instead we just read them out the @@ -905,6 +906,17 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } in.skip(attrLen) + case tpnme.PermittedSubclassesATTR => + sym.setFlag(SEALED) + val numberOfClasses = u2() + numberOfClasses times { + val k = pool.getClassSymbol(u2()) + completer match { + case ctc: ClassTypeCompleter => ctc.permittedSubclasses ::= k // sym.addChild(k) + case _ => + } + } + case _ => in.skip(attrLen) } @@ -1357,6 +1369,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { var exceptions: List[NameOrString] = Nil } private final class ClassTypeCompleter(name: Name, jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter { + var permittedSubclasses: List[symbolTable.Symbol] = Nil override def complete(sym: symbolTable.Symbol): Unit = { val info = if (sig != null) sigToType(sym, sig) else { val superTpe = if (parent == null) definitions.AnyClass.tpe_* else getClassSymbol(parent.value).tpe_* @@ -1365,6 +1378,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { ClassInfoType(superTpe1 :: ifacesTypes, instanceScope, clazz) } sym.setInfo(info) + for (k <- permittedSubclasses) + if (k.parentSymbols.contains(sym)) + sym.addChild(k) } } @@ -1494,7 +1510,13 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { if (flags.isStatic) staticScope else instanceScope } object ClassfileParser { - private implicit class GoodTimes(val n: Int) extends AnyVal { - def times(body: => Unit) = (1 to n).foreach(_ => body) + private implicit class GoodTimes(private val n: Int) extends AnyVal { + def times(body: => Unit): Unit = { + var i = n + while (i > 0) { + body + i -= 1 + } + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 09b2bb644d22..800fef31e99d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -885,7 +885,7 @@ trait ContextErrors extends splain.SplainErrors { // def stabilize def NotAValueError(tree: Tree, sym: Symbol) = { - issueNormalTypeError(tree, sym.kindString + " " + sym.fullName + " is not a value") + issueNormalTypeError(tree, s"${sym.kindString} ${sym.fullName} is not a value") setError(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index a4b54dc2d6f6..f4487e2bfa5e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -427,29 +427,27 @@ trait Namers extends MethodSynthesis { /** Given a ClassDef or ModuleDef, verifies there isn't a companion which * has been defined in a separate file. */ - @nowarn("cat=lint-nonlocal-return") def validateCompanionDefs(tree: ImplDef): Unit = { - val sym = tree.symbol orElse { return } - val ctx = if (context.owner.isPackageObjectClass) context.outer else context - val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name - val clazz = if (sym.isClass) sym else ctx.scope lookupClass tree.name - val fails = ( - module.isModule - && clazz.isClass - && !module.isSynthetic - && !clazz.isSynthetic - && (clazz.sourceFile ne null) - && (module.sourceFile ne null) - && !(module isCoDefinedWith clazz) - && module.exists - && clazz.exists - && (currentRun.compiles(clazz) == currentRun.compiles(module)) - ) - if (fails) { - reporter.error(tree.pos, ( - s"Companions '$clazz' and '$module' must be defined in same file:\n" - + s" Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}") + val sym = tree.symbol + if (sym != NoSymbol) { + val ctx = if (context.owner.isPackageObjectClass) context.outer else context + val module = if (sym.isModule) sym else ctx.scope.lookupModule(tree.name) + val clazz = if (sym.isClass) sym else ctx.scope.lookupClass(tree.name) + val fails = ( + module.isModule + && clazz.isClass + && !module.isSynthetic + && !clazz.isSynthetic + && (clazz.sourceFile ne null) + && (module.sourceFile ne null) + && !module.isCoDefinedWith(clazz) + && module.exists + && clazz.exists + && currentRun.compiles(clazz) == currentRun.compiles(module) ) + if (fails) reporter.error(tree.pos, + sm"""|Companions '$clazz' and '$module' must be defined in same file: + | Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}""") } } @@ -1186,17 +1184,25 @@ trait Namers extends MethodSynthesis { val pending = mutable.ListBuffer[AbsTypeError]() parentTrees foreach { tpt => val ptpe = tpt.tpe - if (!ptpe.isError) { + if (!ptpe.isError && !phase.erasedTypes) { val psym = ptpe.typeSymbol - val sameSourceFile = context.unit.source.file == psym.sourceFile - - if (psym.isSealed && !phase.erasedTypes) - if (sameSourceFile) - psym addChild context.owner + if (psym.isSealed) { + val sameSourceFile = context.unit.source.file == psym.sourceFile + val okChild = + if (psym.isJava) + psym.attachments.get[PermittedSubclassSymbols] match { + case Some(permitted) => permitted.permits.exists(_ == clazz) + case _ => sameSourceFile + } + else + sameSourceFile + if (okChild) + psym.addChild(clazz) else pending += ParentSealedInheritanceError(tpt, psym) - if (psym.isLocalToBlock && psym.isClass && !phase.erasedTypes) - psym addChild context.owner + } + if (psym.isLocalToBlock && psym.isClass) + psym.addChild(clazz) } } pending.foreach(ErrorUtils.issueTypeError) @@ -1214,13 +1220,12 @@ trait Namers extends MethodSynthesis { // add apply and unapply methods to companion objects of case classes, // unless they exist already; here, "clazz" is the module class - if (clazz.isModuleClass) { + if (clazz.isModuleClass) clazz.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass assert(cdef.mods.isCase, "expected case class: "+ cdef) addApplyUnapply(cdef, templateNamer) } - } // add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because // the namer phase must traverse this copy method to create default getters for its parameters. @@ -1266,6 +1271,11 @@ trait Namers extends MethodSynthesis { val res = GenPolyType(tparams0, resultType) val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType) + cdef.getAndRemoveAttachment[PermittedSubclasses].foreach { permitted => + clazz.updateAttachment[PermittedSubclassSymbols] { + PermittedSubclassSymbols(permitted.permits.map(typer.typed(_, Mode.NOmode).symbol)) + } + } // Already assign the type to the class symbol (monoTypeCompleter will do it again). // Allows isDerivedValueClass to look at the info. diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8ac7eef53025..1c66e180e85c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1811,7 +1811,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (parent == DynamicClass) checkFeature(parentPos, currentRun.runDefinitions.DynamicsFeature) def validateParentClass(parent: Tree, superclazz: Symbol) = - if (!parent.isErrorTyped) { + if (!parent.isErrorTyped) { // redundant val psym = parent.tpe.typeSymbol.initialize if (!context.unit.isJava) @@ -1876,7 +1876,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) { val superclazz = parents.head.tpe.typeSymbol - for (p <- parents) validateParentClass(p, superclazz) + parents.foreach(validateParentClass(_, superclazz)) } pending.foreach(ErrorUtils.issueTypeError) @@ -2054,7 +2054,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val body1 = pluginsEnterStats(this, namer.expandMacroAnnotations(templ.body)) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1) if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore - validateParentClasses(parents1, selfType, clazz.isTrait) + validateParentClasses(parents1, selfType, clazz.isTrait) if (clazz.isCase) validateNoCaseAncestor(clazz) if (clazz.isTrait && hasSuperArgs(parents1.head)) @@ -2088,11 +2088,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ctors.foreach(AuxConstrInConstantAnnotation(_, clazz)) } - if (clazz.isTrait) { - for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) { - context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other) - } + for (decl <- clazz.info.decls) + if (decl.isTerm && decl.isEarlyInitialized) + context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other) } treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_* diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index 4212873beaf1..e6473ed074a7 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -291,7 +291,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour List(pathSettings.testParent / norm) } } - .distinct + .distinct.filter(denotesTestPath) } val isRerun = config.optFailed diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 1f80be5f61cb..3089d24483c8 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -154,4 +154,8 @@ trait StdAttachments { case object FieldTypeInferred extends PlainAttachment case class LookupAmbiguityWarning(msg: String) extends PlainAttachment + + /** Java sealed classes may be qualified with a permits clause specifying allowed subclasses. */ + case class PermittedSubclasses(permits: List[Tree]) extends PlainAttachment + case class PermittedSubclassSymbols(permits: List[Symbol]) extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c41d05c658dd..566a352797b9 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -320,6 +320,7 @@ trait StdNames { final val SignatureATTR: NameType = nameType("Signature") final val SourceFileATTR: NameType = nameType("SourceFile") final val SyntheticATTR: NameType = nameType("Synthetic") + final val PermittedSubclassesATTR: NameType = nameType("PermittedSubclasses") final val scala_ : NameType = nameType("scala") @@ -1277,11 +1278,16 @@ trait StdNames { final val keywords = kw.result } - // "The identifiers var, yield, and record are restricted identifiers because they are not allowed in some contexts" - // A type identifier is an identifier that is not the character sequence var, yield, or record. - // An unqualified method identifier is an identifier that is not the character sequence yield. + // The identifiers non-sealed, permits, record, sealed, var, and yield are restricted identifiers + // because they are not allowed in some contexts. + // A type identifier is an identifier that is not the character sequence permits, record, sealed, var, or yield. + // An unqualified method identifier is an identifier that is not the character sequence yield. (JLS 3.8) class JavaRestrictedIdentifiers { + final val PERMITS: TermName = TermName("permits") final val RECORD: TermName = TermName("record") + final val SEALED: TermName = TermName("sealed") + final val UNSEALED: TermName = TermName("non-sealed") + final val NON: TermName = TermName("non") final val VAR: TermName = TermName("var") final val YIELD: TermName = TermName("yield") } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 167835cd08cd..016c7ed6c245 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1011,13 +1011,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isStaticOwner: Boolean = isPackageClass || isModuleClass && isStatic - /** A helper function for isEffectivelyFinal. */ - private def isNotOverridden = - owner.isClass && ( - owner.isEffectivelyFinal - || owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && overridingSymbol(c) == NoSymbol) - ) - /** Is this symbol effectively final? I.e, it cannot be overridden */ final def isEffectivelyFinal: Boolean = ( hasFlag(FINAL | PACKAGE) && this != SingletonClass @@ -1028,7 +1021,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => || isClass && !isRefinementClass && originalOwner.isTerm && children.isEmpty ) /** Is this symbol effectively final or a concrete term member of sealed class whose children do not override it */ - final def isEffectivelyFinalOrNotOverridden: Boolean = isEffectivelyFinal || (isTerm && !isDeferred && isNotOverridden) + final def isEffectivelyFinalOrNotOverridden: Boolean = { + def isNotOverridden = + owner.isClass && ( + owner.isEffectivelyFinal + || owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && overridingSymbol(c) == NoSymbol) + ) + isEffectivelyFinal || isTerm && !isDeferred && isNotOverridden + } /** Is this symbol owned by a package? */ final def isTopLevel = owner.isPackageClass diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 8fb0977b3efc..fff71d710f77 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1426,7 +1426,7 @@ trait Trees extends api.Trees { else Modifiers(flags, privateWithin, newAnns) setPositions positions } - override def toString = "Modifiers(%s, %s, %s)".format(flagString, annotations mkString ", ", positions) + override def toString = s"Modifiers($flagString, ${annotations.mkString(",")}, $positions)" } object Modifiers extends ModifiersExtractor diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 9ab54deaa744..990bb8a438b6 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1769,9 +1769,9 @@ trait Types } } } - //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG + //Console.println(s"baseTypeSeq(${tpe.typeSymbol}) = ${tpe.baseTypeSeqCache.toList}") //DEBUG if (tpe.baseTypeSeqCache eq undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) + throw new TypeError(s"illegal cyclic inheritance involving ${tpe.typeSymbol}") } protected def defineBaseClassesOfCompoundType(tpe: CompoundType): Unit = { @@ -2792,8 +2792,9 @@ trait Types } } } + //Console.println(s"baseTypeSeq(${tpe.typeSymbol}) = ${tpe.baseTypeSeqCache.toList}") //DEBUG if (tpe.baseTypeSeqCache == undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + tpe.sym) + throw new TypeError(s"illegal cyclic inheritance involving ${tpe.sym}") } /** A class representing a method type with parameters. diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 703e000cacd1..36a67706e249 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -80,6 +80,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.TypedExpectingUnitAttachment this.FieldTypeInferred this.LookupAmbiguityWarning + this.PermittedSubclasses + this.PermittedSubclassSymbols this.noPrint this.typeDebug // inaccessible: this.posAssigner diff --git a/test/files/neg/t12159.check b/test/files/neg/t12159.check new file mode 100644 index 000000000000..178017725d11 --- /dev/null +++ b/test/files/neg/t12159.check @@ -0,0 +1,4 @@ +s.scala:11: error: not found: type ZZ +class Z extends ZZ // fail compile: remove when source flags are restored + ^ +1 error diff --git a/test/files/neg/t12159/H.java b/test/files/neg/t12159/H.java new file mode 100644 index 000000000000..3a15309f733e --- /dev/null +++ b/test/files/neg/t12159/H.java @@ -0,0 +1,19 @@ +// javaVersion: 17+ +package p; + +sealed public class H { +} + +final class K extends H { +} + +non-sealed class L extends H { +} + +sealed +class P extends H { +} + +final +class Q extends P { +} diff --git a/test/files/neg/t12159/I.java b/test/files/neg/t12159/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/neg/t12159/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/neg/t12159/J.java b/test/files/neg/t12159/J.java new file mode 100644 index 000000000000..5bd2c4c92374 --- /dev/null +++ b/test/files/neg/t12159/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed public class J implements I permits M { +} diff --git a/test/files/neg/t12159/M.java b/test/files/neg/t12159/M.java new file mode 100644 index 000000000000..245c79304d29 --- /dev/null +++ b/test/files/neg/t12159/M.java @@ -0,0 +1,9 @@ +// javaVersion: 17+ + +package p; + +public final class M extends J { +} + +final class N extends L { +} diff --git a/test/files/neg/t12159/s.scala b/test/files/neg/t12159/s.scala new file mode 100644 index 000000000000..58a01877e3c3 --- /dev/null +++ b/test/files/neg/t12159/s.scala @@ -0,0 +1,11 @@ +// javaVersion: 17+ + +package p + +class S extends H { +} + +trait T extends I { +} + +class Z extends ZZ // fail compile: remove when source flags are restored diff --git a/test/files/neg/t12159b.check b/test/files/neg/t12159b.check new file mode 100644 index 000000000000..14dd6627065d --- /dev/null +++ b/test/files/neg/t12159b.check @@ -0,0 +1,4 @@ +s_2.scala:5: error: illegal inheritance from sealed trait I +class S extends I + ^ +1 error diff --git a/test/files/neg/t12159b/I.java b/test/files/neg/t12159b/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/neg/t12159b/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/neg/t12159b/J.java b/test/files/neg/t12159b/J.java new file mode 100644 index 000000000000..12de6f9fcbd4 --- /dev/null +++ b/test/files/neg/t12159b/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +public final class J implements I { +} diff --git a/test/files/neg/t12159b/s_2.scala b/test/files/neg/t12159b/s_2.scala new file mode 100644 index 000000000000..ec5f40dba180 --- /dev/null +++ b/test/files/neg/t12159b/s_2.scala @@ -0,0 +1,5 @@ +// javaVersion: 17+ + +package p + +class S extends I diff --git a/test/files/neg/t12159c.check b/test/files/neg/t12159c.check new file mode 100644 index 000000000000..189c51ef6817 --- /dev/null +++ b/test/files/neg/t12159c.check @@ -0,0 +1,7 @@ +s_2.scala:7: warning: match may not be exhaustive. +It would fail on the following input: K() + h match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159c/H.java b/test/files/neg/t12159c/H.java new file mode 100644 index 000000000000..bf6394e1e869 --- /dev/null +++ b/test/files/neg/t12159c/H.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class H { +} + +final class J extends H { +} + +final class K extends H { +} + +final class L extends H { +} diff --git a/test/files/neg/t12159c/s_2.scala b/test/files/neg/t12159c/s_2.scala new file mode 100644 index 000000000000..ec9c445cad42 --- /dev/null +++ b/test/files/neg/t12159c/s_2.scala @@ -0,0 +1,12 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(h: H) = + h match { + case j: J => j.toString + case l: L => l.toString + } +} + diff --git a/test/files/pos/t12159/H.java b/test/files/pos/t12159/H.java new file mode 100644 index 000000000000..3a15309f733e --- /dev/null +++ b/test/files/pos/t12159/H.java @@ -0,0 +1,19 @@ +// javaVersion: 17+ +package p; + +sealed public class H { +} + +final class K extends H { +} + +non-sealed class L extends H { +} + +sealed +class P extends H { +} + +final +class Q extends P { +} diff --git a/test/files/pos/t12159/I.java b/test/files/pos/t12159/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/pos/t12159/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/pos/t12159/J.java b/test/files/pos/t12159/J.java new file mode 100644 index 000000000000..5bd2c4c92374 --- /dev/null +++ b/test/files/pos/t12159/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed public class J implements I permits M { +} diff --git a/test/files/pos/t12159/M.java b/test/files/pos/t12159/M.java new file mode 100644 index 000000000000..245c79304d29 --- /dev/null +++ b/test/files/pos/t12159/M.java @@ -0,0 +1,9 @@ +// javaVersion: 17+ + +package p; + +public final class M extends J { +} + +final class N extends L { +} diff --git a/test/files/pos/t12159/s.scala b/test/files/pos/t12159/s.scala new file mode 100644 index 000000000000..29eb9518ea6c --- /dev/null +++ b/test/files/pos/t12159/s.scala @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p + +class S extends L { +} diff --git a/test/files/pos/t12159b/H_1.java b/test/files/pos/t12159b/H_1.java new file mode 100644 index 000000000000..cb3ccb9749fc --- /dev/null +++ b/test/files/pos/t12159b/H_1.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class H_1 { +} + +final class J extends H_1 { +} + +final class K extends H_1 { +} + +final class L extends H_1 { +} diff --git a/test/files/pos/t12159b/s_2.scala b/test/files/pos/t12159b/s_2.scala new file mode 100644 index 000000000000..881204f4d830 --- /dev/null +++ b/test/files/pos/t12159b/s_2.scala @@ -0,0 +1,13 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(h: H_1) = + h match { + case j: J => j.toString + case k: K => k.toString + case l: L => l.toString + } +} + diff --git a/test/files/pos/t12474/Nat.java b/test/files/pos/t12474/Nat.java new file mode 100644 index 000000000000..c9de3f590d84 --- /dev/null +++ b/test/files/pos/t12474/Nat.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +public sealed interface Nat permits Nat.Zero, Nat.Succ { + public static final record Zero() implements Nat {} + public static final record Succ(Nat pred) implements Nat {} +} diff --git a/test/files/pos/t12474/s.scala b/test/files/pos/t12474/s.scala new file mode 100644 index 000000000000..2f4e4ed3a9ad --- /dev/null +++ b/test/files/pos/t12474/s.scala @@ -0,0 +1,5 @@ +// javaVersion: 17+ + +class S { + def j: Nat = new Nat.Zero +} From ad12cb29d39df4a7cdb0f99130eb15b408707f0b Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 24 Apr 2023 17:29:57 -0700 Subject: [PATCH 236/261] Scala 3.3.0-RC4 (was -RC3) --- project/DottySupport.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index 53862b8dc280..a992a9a536ab 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.3.0-RC3" // TASTy version 28.4-1 + val supportedTASTyRelease = "3.3.0-RC4" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.3.0-RC3" + val dottyVersion = "3.3.0-RC4" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( From a18bddeb5b9b121dc34ea533d6d7c9e409695630 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Tue, 25 Apr 2023 19:39:20 +0000 Subject: [PATCH 237/261] Update jackson-module-scala to 2.15.0 in 2.12.x --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 688cc11ed55f..25cdef16d36e 100644 --- a/build.sbt +++ b/build.sbt @@ -413,7 +413,7 @@ lazy val compilerOptionsExporter = Project("compilerOptionsExporter", file(".") .settings(disablePublishing) .settings( libraryDependencies ++= { - val jacksonVersion = "2.14.2" + val jacksonVersion = "2.15.0" Seq( "com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion, "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVersion, From bd15cda80ae237f7ed99cc6047f861f44e44cb58 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 26 Apr 2023 11:38:48 -0700 Subject: [PATCH 238/261] Add caveat for global context to Future --- src/library/scala/concurrent/Future.scala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 3bcedc53a84a..1be6130db645 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -24,11 +24,12 @@ import scala.reflect.ClassTag import scala.concurrent.ExecutionContext.parasitic -/** A `Future` represents a value which may or may not *currently* be available, +/** A `Future` represents a value which may or may not be currently available, * but will be available at some point, or an exception if that value could not be made available. * - * Asynchronous computations that yield futures are created with the `Future.apply` call and are computed using a supplied `ExecutionContext`, - * which can be backed by a Thread pool. + * Asynchronous computations are created by calling `Future.apply`, which yields instances of `Future`. + * Computations are executed using an `ExecutionContext`, which is usually supplied implicitly, + * and which is commonly backed by a thread pool. * * {{{ * import ExecutionContext.Implicits.global @@ -41,6 +42,15 @@ import scala.concurrent.ExecutionContext.parasitic * } * }}} * + * Note that the `global` context is convenient but restricted: + * "fatal" exceptions are reported only by printing a stack trace, + * and the underlying thread pool may be shared by a mix of jobs. + * For any nontrivial application, see the caveats explained at [[ExecutionContext]] + * and also the overview linked below, which explains + * [[https://docs.scala-lang.org/overviews/core/futures.html#exceptions exception handling]] + * in depth. + * + * * @see [[https://docs.scala-lang.org/overviews/core/futures.html Futures and Promises]] * * @define multipleCallbacks @@ -55,8 +65,7 @@ import scala.concurrent.ExecutionContext.parasitic * - `InterruptedException` - not contained within futures * - all `scala.util.control.ControlThrowable` except `NonLocalReturnControl` - not contained within futures * - * Instead, the future is completed with a ExecutionException with one of the exceptions above - * as the cause. + * Instead, the future is completed with an ExecutionException that has one of the exceptions above as its cause. * If a future is failed with a `scala.runtime.NonLocalReturnControl`, * it is completed with a value from that throwable instead. * From 727be50a6c1a465782b50900e52b8d51cae5fe06 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 26 Apr 2023 19:19:14 -0700 Subject: [PATCH 239/261] adjust tests on JDK 17+ for PermittedSubclasses change references #10105 --- test/files/neg/t8700b-new.check | 11 +++++++++++ test/files/neg/t8700b-new/Bar_2.scala | 11 +++++++++++ test/files/neg/t8700b-new/Baz_1.java | 12 ++++++++++++ test/files/neg/t8700b-new/Foo_1.java | 5 +++++ test/files/neg/{t8700b.check => t8700b-old.check} | 4 ++-- test/files/neg/{t8700b => t8700b-old}/Bar_2.scala | 1 + test/files/neg/{t8700b => t8700b-old}/Baz_1.java | 1 + test/files/neg/{t8700b => t8700b-old}/Foo_1.java | 1 + 8 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t8700b-new.check create mode 100644 test/files/neg/t8700b-new/Bar_2.scala create mode 100644 test/files/neg/t8700b-new/Baz_1.java create mode 100644 test/files/neg/t8700b-new/Foo_1.java rename test/files/neg/{t8700b.check => t8700b-old.check} (72%) rename test/files/neg/{t8700b => t8700b-old}/Bar_2.scala (90%) rename test/files/neg/{t8700b => t8700b-old}/Baz_1.java (90%) rename test/files/neg/{t8700b => t8700b-old}/Foo_1.java (66%) diff --git a/test/files/neg/t8700b-new.check b/test/files/neg/t8700b-new.check new file mode 100644 index 000000000000..172c0d0fab78 --- /dev/null +++ b/test/files/neg/t8700b-new.check @@ -0,0 +1,11 @@ +Bar_2.scala:4: warning: match may not be exhaustive. +It would fail on the following input: B + def bar1(foo: Foo_1) = foo match { + ^ +Bar_2.scala:8: warning: match may not be exhaustive. +It would fail on the following inputs: (_ : Baz_1$1), (_ : Baz_1$2), B + def bar2(foo: Baz_1) = foo match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t8700b-new/Bar_2.scala b/test/files/neg/t8700b-new/Bar_2.scala new file mode 100644 index 000000000000..36e1e6d2ccc5 --- /dev/null +++ b/test/files/neg/t8700b-new/Bar_2.scala @@ -0,0 +1,11 @@ +// javaVersion: 17+ +// scalac: -Werror +object Bar { + def bar1(foo: Foo_1) = foo match { + case Foo_1.A => 1 + } + + def bar2(foo: Baz_1) = foo match { + case Baz_1.A => 1 + } +} diff --git a/test/files/neg/t8700b-new/Baz_1.java b/test/files/neg/t8700b-new/Baz_1.java new file mode 100644 index 000000000000..9398a24b5714 --- /dev/null +++ b/test/files/neg/t8700b-new/Baz_1.java @@ -0,0 +1,12 @@ +// javaVersion: 17+ +public enum Baz_1 { + A { + public void baz1() {} + }, + B { + public void baz1() {} + }; + + public abstract void baz1(); + public void baz2() {} +} diff --git a/test/files/neg/t8700b-new/Foo_1.java b/test/files/neg/t8700b-new/Foo_1.java new file mode 100644 index 000000000000..8694131d3eab --- /dev/null +++ b/test/files/neg/t8700b-new/Foo_1.java @@ -0,0 +1,5 @@ +// javaVersion: 17+ +public enum Foo_1 { + A, + B +} diff --git a/test/files/neg/t8700b.check b/test/files/neg/t8700b-old.check similarity index 72% rename from test/files/neg/t8700b.check rename to test/files/neg/t8700b-old.check index 65af1192f801..e061c0ce443b 100644 --- a/test/files/neg/t8700b.check +++ b/test/files/neg/t8700b-old.check @@ -1,8 +1,8 @@ -Bar_2.scala:3: warning: match may not be exhaustive. +Bar_2.scala:4: warning: match may not be exhaustive. It would fail on the following input: B def bar1(foo: Foo_1) = foo match { ^ -Bar_2.scala:7: warning: match may not be exhaustive. +Bar_2.scala:8: warning: match may not be exhaustive. It would fail on the following input: B def bar2(foo: Baz_1) = foo match { ^ diff --git a/test/files/neg/t8700b/Bar_2.scala b/test/files/neg/t8700b-old/Bar_2.scala similarity index 90% rename from test/files/neg/t8700b/Bar_2.scala rename to test/files/neg/t8700b-old/Bar_2.scala index 6e0114d76c9e..43b5463468f0 100644 --- a/test/files/neg/t8700b/Bar_2.scala +++ b/test/files/neg/t8700b-old/Bar_2.scala @@ -1,3 +1,4 @@ +// javaVersion: 8 // scalac: -Werror object Bar { def bar1(foo: Foo_1) = foo match { diff --git a/test/files/neg/t8700b/Baz_1.java b/test/files/neg/t8700b-old/Baz_1.java similarity index 90% rename from test/files/neg/t8700b/Baz_1.java rename to test/files/neg/t8700b-old/Baz_1.java index 6a057c2c9c07..6373780810b6 100644 --- a/test/files/neg/t8700b/Baz_1.java +++ b/test/files/neg/t8700b-old/Baz_1.java @@ -1,3 +1,4 @@ +// javaVersion: 8 public enum Baz_1 { A { public void baz1() {} diff --git a/test/files/neg/t8700b/Foo_1.java b/test/files/neg/t8700b-old/Foo_1.java similarity index 66% rename from test/files/neg/t8700b/Foo_1.java rename to test/files/neg/t8700b-old/Foo_1.java index 22656bdeddff..aa92556676f0 100644 --- a/test/files/neg/t8700b/Foo_1.java +++ b/test/files/neg/t8700b-old/Foo_1.java @@ -1,3 +1,4 @@ +// javaVersion: 8 public enum Foo_1 { A, B From 8a612d91e2b507aa84aa955d4688816643112f57 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Thu, 27 Apr 2023 18:33:03 +0000 Subject: [PATCH 240/261] Update scala3-compiler_3, ... to 3.3.0-RC5 --- project/DottySupport.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index a992a9a536ab..96f0346748cb 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.3.0-RC4" // TASTy version 28.4-1 + val supportedTASTyRelease = "3.3.0-RC5" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.3.0-RC4" + val dottyVersion = "3.3.0-RC5" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( From a0f546d8c98a2c1a5f355710936b2ec8edcabe62 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 5 Aug 2022 10:16:05 -0700 Subject: [PATCH 241/261] Populate children of Java `sealed` hierarchy Required for checking permitted subclasses and for exhaustiveness of patterns. Co-authored-by: Lukas Rytz --- .../scala/tools/nsc/javac/JavaParsers.scala | 6 +++- .../transform/patmat/PatternMatching.scala | 6 +++- .../scala/tools/nsc/typechecker/Namers.scala | 2 ++ .../scala/tools/nsc/typechecker/Typers.scala | 28 +++++++++++++++++-- test/files/neg/t12159d.check | 7 +++++ test/files/neg/t12159d/X.java | 14 ++++++++++ test/files/neg/t12159d/t.scala | 12 ++++++++ test/files/neg/t12159e.check | 11 ++++++++ test/files/neg/t12159e/X.java | 20 +++++++++++++ test/files/neg/t12159e/t.scala | 18 ++++++++++++ test/files/neg/t12159f.check | 11 ++++++++ test/files/neg/t12159f/X.java | 14 ++++++++++ test/files/neg/t12159f/Z.java | 8 ++++++ test/files/neg/t12159f/t.scala | 18 ++++++++++++ test/files/neg/t12159g.check | 7 +++++ test/files/neg/t12159g/X.java | 12 ++++++++ test/files/neg/t12159g/t.scala | 5 ++++ 17 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 test/files/neg/t12159d.check create mode 100644 test/files/neg/t12159d/X.java create mode 100644 test/files/neg/t12159d/t.scala create mode 100644 test/files/neg/t12159e.check create mode 100644 test/files/neg/t12159e/X.java create mode 100644 test/files/neg/t12159e/t.scala create mode 100644 test/files/neg/t12159f.check create mode 100644 test/files/neg/t12159f/X.java create mode 100644 test/files/neg/t12159f/Z.java create mode 100644 test/files/neg/t12159f/t.scala create mode 100644 test/files/neg/t12159g.check create mode 100644 test/files/neg/t12159g/X.java create mode 100644 test/files/neg/t12159g/t.scala diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index c6894afba4df..e77576c804ae 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -19,6 +19,7 @@ package javac import symtab.Flags import JavaTokens._ import scala.annotation._ +import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.language.implicitConversions import scala.reflect.internal.util.{ListOfNil, Position} @@ -45,6 +46,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { abstract class JavaParser extends ParserCommon { val in: JavaScanner + def unit: CompilationUnit def freshName(prefix : String): Name protected implicit def i2p(offset : Int) : Position @@ -494,7 +496,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { in.nextToken() case _ => val unsealed = 0L // no flag for UNSEALED - def consume(added: FlagSet): false = { in.nextToken(); /*flags |= added;*/ false } + def consume(added: FlagSet): false = { in.nextToken(); flags |= added; false } def lookingAhead(s: String): Boolean = { import scala.reflect.internal.Chars._ var i = 0 @@ -838,6 +840,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { else Nil def classDecl(mods: Modifiers): List[Tree] = { + if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty accept(CLASS) val pos = in.currentPos val name = identForType() @@ -904,6 +907,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } def interfaceDecl(mods: Modifiers): List[Tree] = { + if (mods.hasFlag(SEALED)) patmat.javaClassesByUnit(unit.source) = mutable.Set.empty accept(INTERFACE) val pos = in.currentPos val name = identForType() diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index b9d562ff9756..958ffad37c3d 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -13,9 +13,10 @@ package scala.tools.nsc.transform.patmat import scala.annotation.tailrec +import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.reflect.internal.{Mode, Types} -import scala.reflect.internal.util.Statistics +import scala.reflect.internal.util.{SourceFile, Statistics} import scala.tools.nsc.Global import scala.tools.nsc.ast import scala.tools.nsc.transform.{Transform, TypingTransformers} @@ -58,6 +59,9 @@ trait PatternMatching extends Transform val phaseName: String = "patmat" + /** Symbols to force for determining children of sealed Java classes. */ + val javaClassesByUnit = perRunCaches.newMap[SourceFile, mutable.Set[Symbol]]() + def newTransformer(unit: CompilationUnit): AstTransformer = new MatchTransformer(unit) class MatchTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index f4487e2bfa5e..6323357a5c5b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -795,6 +795,8 @@ trait Namers extends MethodSynthesis { tree.symbol = enterClassSymbol(tree) tree.symbol setInfo completerOf(tree) + if (tree.symbol.isJava) patmat.javaClassesByUnit.get(tree.symbol.pos.source).foreach(_.addOne(tree.symbol)) + if (mods.isCase) { val m = ensureCompanionObject(tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1c66e180e85c..f211616c271b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -15,12 +15,11 @@ package tools.nsc package typechecker import scala.annotation.{tailrec, unused} -import scala.collection.mutable +import scala.collection.mutable, mutable.ListBuffer import scala.reflect.internal.{Chars, TypesStats} import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StringContextStripMarginOps} import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory} import scala.util.chaining._ -import mutable.ListBuffer import symtab.Flags._ import Mode._ @@ -2653,6 +2652,31 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector) val casesTyped = typedCases(cases, selectorTp, pt) + def initChildren(sym: Symbol): Unit = + if (sym.isJava && sym.isSealed) + sym.attachments.get[PermittedSubclassSymbols] match { + case Some(PermittedSubclassSymbols(permits)) => + for (child <- permits if child.isJava) + initChildren(child.initialize) + case _ => + val seen = mutable.HashSet.empty[Symbol] + def populate(): Unit = + patmat.javaClassesByUnit.get(sym.pos.source) match { + case Some(classes) => + classes.find(!seen(_)) match { + case Some(unseen) => + seen += unseen + unseen.initialize.companionSymbol.moduleClass.initialize + if (unseen.hasAttachment[PermittedSubclassSymbols]) initChildren(unseen) + populate() + case _ => + } + case _ => + } + populate() + } + initChildren(selectorTp.typeSymbol) + def finish(cases: List[CaseDef], matchType: Type) = treeCopy.Match(tree, selector1, cases) setType matchType diff --git a/test/files/neg/t12159d.check b/test/files/neg/t12159d.check new file mode 100644 index 000000000000..1efb798c80d7 --- /dev/null +++ b/test/files/neg/t12159d.check @@ -0,0 +1,7 @@ +t.scala:7: warning: match may not be exhaustive. +It would fail on the following input: W() + x match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159d/X.java b/test/files/neg/t12159d/X.java new file mode 100644 index 000000000000..0812a6fd21cd --- /dev/null +++ b/test/files/neg/t12159d/X.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class X { +} + +final class W extends X { +} + +final class Y extends X { +} + +final class Z extends X { +} diff --git a/test/files/neg/t12159d/t.scala b/test/files/neg/t12159d/t.scala new file mode 100644 index 000000000000..e57c9cd62ddc --- /dev/null +++ b/test/files/neg/t12159d/t.scala @@ -0,0 +1,12 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(x: X) = + x match { + case y: Y => y.toString + case z: Z => z.toString + } +} + diff --git a/test/files/neg/t12159e.check b/test/files/neg/t12159e.check new file mode 100644 index 000000000000..86807203f124 --- /dev/null +++ b/test/files/neg/t12159e.check @@ -0,0 +1,11 @@ +t.scala:7: warning: match may not be exhaustive. +It would fail on the following input: W() + x match { + ^ +t.scala:12: warning: match may not be exhaustive. +It would fail on the following inputs: Z(), Z2() + x match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12159e/X.java b/test/files/neg/t12159e/X.java new file mode 100644 index 000000000000..6b770b115ea6 --- /dev/null +++ b/test/files/neg/t12159e/X.java @@ -0,0 +1,20 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class X { +} + +final class W extends X { +} + +final class Y extends X { +} + +sealed class Z extends X permits Z1, Z2 { +} + +final class Z1 extends Z { +} + +final class Z2 extends Z { +} diff --git a/test/files/neg/t12159e/t.scala b/test/files/neg/t12159e/t.scala new file mode 100644 index 000000000000..1d5810a09f85 --- /dev/null +++ b/test/files/neg/t12159e/t.scala @@ -0,0 +1,18 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(x: X) = + x match { + case y: Y => y.toString + case z: Z => z.toString + } + def g(x: X) = + x match { + case w: W => w.toString + case y: Y => y.toString + case z: Z1 => z.toString + } +} + diff --git a/test/files/neg/t12159f.check b/test/files/neg/t12159f.check new file mode 100644 index 000000000000..86807203f124 --- /dev/null +++ b/test/files/neg/t12159f.check @@ -0,0 +1,11 @@ +t.scala:7: warning: match may not be exhaustive. +It would fail on the following input: W() + x match { + ^ +t.scala:12: warning: match may not be exhaustive. +It would fail on the following inputs: Z(), Z2() + x match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12159f/X.java b/test/files/neg/t12159f/X.java new file mode 100644 index 000000000000..bc1043b66039 --- /dev/null +++ b/test/files/neg/t12159f/X.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class X { +} + +final class W extends X { +} + +final class Y extends X { +} + +sealed class Z extends X permits Z1, Z2 { +} diff --git a/test/files/neg/t12159f/Z.java b/test/files/neg/t12159f/Z.java new file mode 100644 index 000000000000..55fcc661a182 --- /dev/null +++ b/test/files/neg/t12159f/Z.java @@ -0,0 +1,8 @@ +// javaVersion: 17+ +package p; + +final class Z1 extends Z { +} + +final class Z2 extends Z { +} diff --git a/test/files/neg/t12159f/t.scala b/test/files/neg/t12159f/t.scala new file mode 100644 index 000000000000..1d5810a09f85 --- /dev/null +++ b/test/files/neg/t12159f/t.scala @@ -0,0 +1,18 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(x: X) = + x match { + case y: Y => y.toString + case z: Z => z.toString + } + def g(x: X) = + x match { + case w: W => w.toString + case y: Y => y.toString + case z: Z1 => z.toString + } +} + diff --git a/test/files/neg/t12159g.check b/test/files/neg/t12159g.check new file mode 100644 index 000000000000..f268b3430cdc --- /dev/null +++ b/test/files/neg/t12159g.check @@ -0,0 +1,7 @@ +t.scala:4: warning: match may not be exhaustive. +It would fail on the following inputs: Oz(), Z() + def n(a: X) = a match { case _: Y => 42 } + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159g/X.java b/test/files/neg/t12159g/X.java new file mode 100644 index 000000000000..8687ede65157 --- /dev/null +++ b/test/files/neg/t12159g/X.java @@ -0,0 +1,12 @@ + +package p; +public sealed interface X { + public default int x() { return 27; } +} +final class Y implements X { } +final class O { + final static class Z implements X { } + final static class Inner { + final static class Oz implements X { } + } +} diff --git a/test/files/neg/t12159g/t.scala b/test/files/neg/t12159g/t.scala new file mode 100644 index 000000000000..f92009287f1b --- /dev/null +++ b/test/files/neg/t12159g/t.scala @@ -0,0 +1,5 @@ +// scalac: -Werror -Xlint +package p +class T { + def n(a: X) = a match { case _: Y => 42 } +} From 808d1fbe5b27b35f3a463569f665499579dd904b Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 1 May 2023 16:02:27 +0200 Subject: [PATCH 242/261] Restore test fixed by recent Java-sealed PR --- test/files/neg/t12159.check | 9 ++++++--- test/files/neg/t12159/s.scala | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/test/files/neg/t12159.check b/test/files/neg/t12159.check index 178017725d11..bda2e48622ce 100644 --- a/test/files/neg/t12159.check +++ b/test/files/neg/t12159.check @@ -1,4 +1,7 @@ -s.scala:11: error: not found: type ZZ -class Z extends ZZ // fail compile: remove when source flags are restored +s.scala:5: error: illegal inheritance from sealed class H +class S extends H { ^ -1 error +s.scala:8: error: illegal inheritance from sealed trait I +trait T extends I { + ^ +2 errors diff --git a/test/files/neg/t12159/s.scala b/test/files/neg/t12159/s.scala index 58a01877e3c3..b8ec52070d07 100644 --- a/test/files/neg/t12159/s.scala +++ b/test/files/neg/t12159/s.scala @@ -7,5 +7,3 @@ class S extends H { trait T extends I { } - -class Z extends ZZ // fail compile: remove when source flags are restored From 2f2d4c6e82b1430b112796a758777dc188295067 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 2 May 2023 19:40:21 -0700 Subject: [PATCH 243/261] update .mailmap for 2.12.18 release --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 52a44cd06735..595313a5015d 100644 --- a/.mailmap +++ b/.mailmap @@ -67,6 +67,7 @@ Pavel Pavlov Philipp Haller Philipp Haller Philippe Altherr +Philippus Baalman Raphaël Noir Roland Kuhn Rüdiger Klaehn From bb50eed0d7e855a5743f2edea461e93f5c13ded3 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 3 May 2023 10:06:13 -0700 Subject: [PATCH 244/261] Handle URISyntaxException --- src/compiler/scala/tools/nsc/util/ClassPath.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 94bdbaaa0fc7..291b0422f5d8 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -14,8 +14,7 @@ package scala.tools.nsc package util import io.{AbstractFile, Directory, File, Jar} -import java.net.MalformedURLException -import java.net.{URI, URL} +import java.net.{MalformedURLException, URI, URISyntaxException, URL} import java.util.regex.PatternSyntaxException import File.pathSeparator @@ -185,7 +184,7 @@ object ClassPath { def specToURL(spec: String): Option[URL] = try Some(new URI(spec).toURL) - catch { case _: MalformedURLException => None } + catch { case _: MalformedURLException | _: URISyntaxException => None } def manifests: List[java.net.URL] = { import scala.jdk.CollectionConverters._ From a100d1810ae8b79be55431c6e92e04ac72397c11 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Sun, 14 May 2023 19:53:34 -0700 Subject: [PATCH 245/261] Scala 3.3.0-RC6 (was -RC5) --- project/DottySupport.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index 96f0346748cb..b27b3a445ddd 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.3.0-RC5" // TASTy version 28.4-1 + val supportedTASTyRelease = "3.3.0-RC6" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.3.0-RC5" + val dottyVersion = "3.3.0-RC6" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( From 52ce4b1ad9e0a22d14ad71d99413657df1c849cb Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Mon, 15 May 2023 20:31:05 -0700 Subject: [PATCH 246/261] ClassfileParser: allow missing param names (for JDK 21) fixes scala/bug#12783 --- .../tools/nsc/symtab/classfile/ClassfileParser.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 30638b822877..53132a16eb44 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -825,7 +825,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { val paramNameAccess = new Array[Int](paramCount) var i = 0 while (i < paramCount) { - paramNames(i) = pool.getExternalName(u2) + paramNames(i) = u2() match { + case 0 => null // may occur on JDK 21+, as per scala/bug#12783 + case index => pool.getExternalName(index) + } paramNameAccess(i) = u2 i += 1 } @@ -1248,6 +1251,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { sym setInfo createFromClonedSymbols(alias.initialize.typeParams, alias.tpe)(typeFun) } } + // on JDK 21+, `names` may include nulls, as per scala/bug#12783 private class ParamNames(val names: Array[NameOrString], val access: Array[Int]) { assert(names.length == access.length) def length = names.length @@ -1351,8 +1355,10 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { case (i, param) => val isSynthetic = (paramNames.access(i) & ACC_SYNTHETIC) != 0 if (!isSynthetic) { - param.name = paramNames.names(i).name.toTermName.encode param.resetFlag(SYNTHETIC) + val nameOrString = paramNames.names(i) + if (nameOrString != null) + param.name = nameOrString.name.toTermName.encode } } // there's not anything we can do, but it's slightly worrisome From 15a0ddabb3a036f3f8f6ee1328ddddd9b1243e7d Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 16 May 2023 07:47:24 -0700 Subject: [PATCH 247/261] Lambdaless test is more forgiving --- src/partest/scala/tools/partest/ReplTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/partest/scala/tools/partest/ReplTest.scala b/src/partest/scala/tools/partest/ReplTest.scala index 33060793a4cb..e4f2aaabe505 100644 --- a/src/partest/scala/tools/partest/ReplTest.scala +++ b/src/partest/scala/tools/partest/ReplTest.scala @@ -89,7 +89,7 @@ trait Lambdaless extends ReplTest { override def normalize(s: String) = stripLambdaClassName(super.normalize(s)) } object Lambdaless { - private val lambdaless = """\$Lambda\$\d+/(?:0x[a-f0-9]{16}|\d+)(@[a-fA-F0-9]+)?""".r + private val lambdaless = """\$Lambda(?:\$\d+)?/(?:0x[a-f0-9]{16}|\d+)(?:@[a-fA-F0-9]+)?""".r private def stripLambdaClassName(s: String): String = lambdaless.replaceAllIn(s, Regex.quoteReplacement("")) } From ca6b46f9049294cd00973afb53c1488a2c8b671e Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 8 Mar 2023 15:15:44 +0100 Subject: [PATCH 248/261] Include top-level symbols from same file in outer ambiguity error --- spec/02-identifiers-names-and-scopes.md | 13 ++++++++----- .../scala/tools/nsc/typechecker/Contexts.scala | 2 +- test/files/jvm/protectedacc.scala | 4 ++-- test/files/neg/t11921b.check | 9 ++++++++- test/files/neg/t11921b.scala | 9 ++++++++- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/spec/02-identifiers-names-and-scopes.md b/spec/02-identifiers-names-and-scopes.md index 8ae89aec1178..213c2bee96df 100644 --- a/spec/02-identifiers-names-and-scopes.md +++ b/spec/02-identifiers-names-and-scopes.md @@ -17,11 +17,14 @@ which are collectively called _bindings_. Bindings of each kind are assigned a precedence which determines whether one binding can shadow another: -1. Definitions and declarations in lexical scope that are not [top-level](09-top-level-definitions.html) - have the highest precedence. -1. Definitions and declarations that are either inherited, - or made available by a package clause and also defined in the same compilation unit as the reference to them, - have the next highest precedence. + +1. Definitions and declarations that are local, inherited, or made + available by a package clause and also defined in the same compilation unit + as the reference to them, have the highest precedence. 1. Explicit imports have the next highest precedence. 1. Wildcard imports have the next highest precedence. 1. Bindings made available by a package clause, diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 6ff69b02252b..2f9f1b4e678b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1612,7 +1612,7 @@ trait Contexts { self: Analyzer => val wasFoundInSuper = foundInSuper val foundCompetingSymbol: () => Boolean = if (foreignDefined) () => !foreignDefined - else () => !defSym.isTopLevel && !defSym.owner.isPackageObjectOrClass && !foundInSuper && !foreignDefined + else () => !defSym.owner.isPackageObjectOrClass && !foundInSuper && !foreignDefined while ((cx ne NoContext) && cx.depth >= symbolDepth) cx = cx.outer if (wasFoundInSuper) while ((cx ne NoContext) && (cx.owner eq cx0.owner)) cx = cx.outer diff --git a/test/files/jvm/protectedacc.scala b/test/files/jvm/protectedacc.scala index 43d218fa89fd..51a50bed421c 100644 --- a/test/files/jvm/protectedacc.scala +++ b/test/files/jvm/protectedacc.scala @@ -47,7 +47,7 @@ package p { abstract class PolyA[a] { protected def m(x: a): Unit; - class B { + class BB { trait Node { def s: String = ""; @@ -134,7 +134,7 @@ package p { abstract class X[T] extends PolyA[T] { - trait Inner extends B { + trait Inner extends BB { def self: T; def self2: Node; def getB: Inner; diff --git a/test/files/neg/t11921b.check b/test/files/neg/t11921b.check index 0e2e2391d349..045f13ffb9a2 100644 --- a/test/files/neg/t11921b.check +++ b/test/files/neg/t11921b.check @@ -29,6 +29,13 @@ Such references are ambiguous in Scala 3. To continue using the inherited symbol Or use `-Wconf:msg=legacy-binding:s` to silence this warning. println(y) // error ^ +t11921b.scala:65: warning: reference to global is ambiguous; +it is both defined in the enclosing package and inherited in the enclosing object D as value global (defined in class C) +In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. +Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.global`. +Or use `-Wconf:msg=legacy-binding:s` to silence this warning. + println(global) // error + ^ t11921b.scala:75: warning: reference to x is ambiguous; it is both defined in the enclosing object Uhu and inherited in the enclosing class C as value x (defined in class A, inherited through parent class B) In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. @@ -50,5 +57,5 @@ Such references are ambiguous in Scala 3. To continue using the inherited symbol Or use `-Wconf:msg=legacy-binding:s` to silence this warning. def v = t(lo) // error ^ -7 warnings +8 warnings 1 error diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index 93447b5e0a5f..ef3b53ae4724 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -62,7 +62,7 @@ class C { val global = 42 } object D extends C { - println(global) // OK, since global is defined in package (https://github.com/scala/scala/pull/10220/files#r1109773904) + println(global) // error } object test5 { @@ -136,3 +136,10 @@ object test10 { def v = t(lo) // error } } + +package scala { + trait P { trait Option[+A] } + class C extends P { + def t = new Option[String] {} // OK, competing scala.Option is not defined in the same compilation unit + } +} From b8c58b1abe017ce739a6e592b1d3603baadecf73 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 2 May 2023 10:06:09 +0200 Subject: [PATCH 249/261] Demote the new ambiguity: nothing in 2.13.11, warn (not err) under Xsource:3 --- src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 9 +++++---- test/files/neg/t11921-alias.scala | 2 +- test/files/neg/t11921.scala | 2 +- test/files/neg/t11921b.scala | 2 +- test/files/neg/t11921c.scala | 2 +- test/files/pos/t11921b.scala | 2 +- test/files/pos/t11921c.scala | 1 + 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 2f9f1b4e678b..64b902efe302 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1377,16 +1377,17 @@ trait Contexts { self: Analyzer => sm"""|it is both defined in the enclosing ${outer1.owner} and inherited in the enclosing $classDesc as $inherited1 (defined in ${inherited1.ownsString}$inherit) |In Scala 2, symbols inherited from a superclass shadow symbols defined in an outer scope. |Such references are ambiguous in Scala 3. To continue using the inherited symbol, write `this.${outer1.name}`.""" - if (currentRun.isScala3) - Some(LookupAmbiguous(message)) - else { + // For now (2.13.11), warn under Xsource:3. We'll consider warning by default (and erring in Xsource:3) in 2.13.12 + if (currentRun.isScala3) { // passing the message to `typedIdent` as attachment, we don't have the position here to report the warning inherited.updateAttachment(LookupAmbiguityWarning( sm"""|reference to ${outer1.name} is ambiguous; |$message |Or use `-Wconf:msg=legacy-binding:s` to silence this warning.""")) + // Some(LookupAmbiguous(message)) // to make it an error in 2.13.12 + None + } else None - } } } else Some(LookupAmbiguous(s"it is both defined in ${outer.owner} and available as ${inherited.fullLocationString}")) diff --git a/test/files/neg/t11921-alias.scala b/test/files/neg/t11921-alias.scala index f238796da174..fab5ae9badc2 100644 --- a/test/files/neg/t11921-alias.scala +++ b/test/files/neg/t11921-alias.scala @@ -1,4 +1,4 @@ -// scalac: -Werror +// scalac: -Werror -Xsource:3 object t1 { class C[T] { type TT = T } diff --git a/test/files/neg/t11921.scala b/test/files/neg/t11921.scala index 01269d3895f1..91093f3e7443 100644 --- a/test/files/neg/t11921.scala +++ b/test/files/neg/t11921.scala @@ -1,4 +1,4 @@ - +// scalac: -Xsource:3 class C { def lazyMap[A, B](coll: Iterable[A], f: A => B) = diff --git a/test/files/neg/t11921b.scala b/test/files/neg/t11921b.scala index ef3b53ae4724..887b7280698c 100644 --- a/test/files/neg/t11921b.scala +++ b/test/files/neg/t11921b.scala @@ -1,4 +1,4 @@ -// scalac: -Werror +// scalac: -Werror -Xsource:3 object test1 { diff --git a/test/files/neg/t11921c.scala b/test/files/neg/t11921c.scala index f528773d7554..3b38fa6c9520 100644 --- a/test/files/neg/t11921c.scala +++ b/test/files/neg/t11921c.scala @@ -1,4 +1,4 @@ -// scalac: -Wconf:msg=legacy-binding:s +// scalac: -Wconf:msg=legacy-binding:s -Xsource:3 class C { def lazyMap[A, B](coll: Iterable[A], f: A => B) = diff --git a/test/files/pos/t11921b.scala b/test/files/pos/t11921b.scala index b6c0ea293d04..38870f0cb53b 100644 --- a/test/files/pos/t11921b.scala +++ b/test/files/pos/t11921b.scala @@ -1,4 +1,4 @@ -// scalac: -Werror -Wconf:msg=legacy-binding:s +// scalac: -Werror -Wconf:msg=legacy-binding:s -Xsource:3 object test1 { diff --git a/test/files/pos/t11921c.scala b/test/files/pos/t11921c.scala index d73c4c31536e..d78439424f4e 100644 --- a/test/files/pos/t11921c.scala +++ b/test/files/pos/t11921c.scala @@ -1,3 +1,4 @@ +// scalac: -Xsource:3 // test/scaladoc/resources/t5784.scala From cce8a01034fb0a2635f7017a3d080e9ec6ac5584 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 10 May 2023 15:46:10 -0700 Subject: [PATCH 250/261] add JDK 21 Early Access to nightly CI --- .github/workflows/ci.yml | 15 ++++++++++++--- .travis.yml | 4 ++-- project/build.properties | 2 +- scripts/common | 2 +- test/files/neg/macro-exception.check | 5 ++++- test/files/neg/macro-invalidret.check | 5 ++++- test/files/run/sammy_after_implicit_view.scala | 2 +- test/files/run/sammy_restrictions_LMF.scala | 2 +- versions.properties | 2 +- 9 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 052325d9192a..93dd2a432518 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,16 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] + java-distribution: [temurin] java: [8, 11, 17, 20] + # 21-ea will presumably be available from Temurin eventually, but for now: + include: + - os: ubuntu-latest + java-distribution: zulu + java: 21-ea + - os: windows-latest + java-distribution: zulu + java: 21-ea runs-on: ${{matrix.os}} steps: - run: git config --global core.autocrlf false @@ -26,15 +35,15 @@ jobs: - name: Setup Java uses: actions/setup-java@v3 with: - distribution: temurin + distribution: ${{matrix.java-distribution}} java-version: ${{matrix.java}} cache: sbt - name: Build run: | - sbt setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal + sbt -Dsbt.scala.version=2.12.18-M2 setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal - name: Test run: | STARR=`cat buildcharacter.properties | grep ^maven.version.number | cut -d= -f2` && echo $STARR - sbt -Dstarr.version=$STARR setupValidateTest test:compile info testAll + sbt -Dsbt.scala.version=2.12.18-M2 -Dstarr.version=$STARR setupValidateTest test:compile info testAll diff --git a/.travis.yml b/.travis.yml index 67102ca1fc07..50e45ce7f3b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,9 +40,9 @@ jobs: name: "JDK 8 pr validation" if: type = pull_request script: - - sbt -warn setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal + - sbt -Dsbt.scala.version=2.12.18-M2 -warn setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal - STARR=`cat buildcharacter.properties | grep ^maven.version.number | cut -d= -f2` && echo $STARR - - sbt -Dstarr.version=$STARR -warn setupValidateTest test:compile info testAll + - sbt -Dsbt.scala.version=2.12.18-M2 -Dstarr.version=$STARR -warn setupValidateTest test:compile info testAll # build the spec using jekyll - stage: build diff --git a/project/build.properties b/project/build.properties index 46e43a97ed86..72413de151e1 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.8.3 diff --git a/scripts/common b/scripts/common index 425fe9fcd575..7ac5624d83b1 100644 --- a/scripts/common +++ b/scripts/common @@ -17,7 +17,7 @@ mkdir -p "$WORKSPACE/resolutionScratch_" SBT_VERSION=`grep sbt.version $WORKSPACE/project/build.properties | sed -n 's/sbt.version=\(.*\)/\1/p'` SBT_CMD=${SBT_CMD-sbt} -SBT_CMD="$SBT_CMD -sbt-version $SBT_VERSION" +SBT_CMD="$SBT_CMD -Dsbt.scala.version=2.12.18-M2 -sbt-version $SBT_VERSION" # repo to publish builds integrationRepoUrl=${integrationRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-integration/"} diff --git a/test/files/neg/macro-exception.check b/test/files/neg/macro-exception.check index d2249d9b9ea9..7d7061c5a3de 100644 --- a/test/files/neg/macro-exception.check +++ b/test/files/neg/macro-exception.check @@ -1,8 +1,11 @@ Test_2.scala:2: error: exception during macro expansion: java.lang.Exception at Macros$.impl(Macros_1.scala:6) -#partest java20+ +#partest java20 at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) +#partest java21+ + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) +#partest java20+ at java.base/java.lang.reflect.Method.invoke(Method.java:578) at scala.reflect.macros.runtime.JavaReflectionRuntimes$JavaReflectionResolvers.$anonfun$resolveJavaReflectionRuntime$6(JavaReflectionRuntimes.scala:51) at scala.tools.nsc.typechecker.Macros.macroExpandWithRuntime(Macros.scala:849) diff --git a/test/files/neg/macro-invalidret.check b/test/files/neg/macro-invalidret.check index 286f556c9d3c..406f9f23d32a 100644 --- a/test/files/neg/macro-invalidret.check +++ b/test/files/neg/macro-invalidret.check @@ -18,8 +18,11 @@ Macros_Test_2.scala:7: error: macro defs must have explicitly specified return t Macros_Test_2.scala:15: error: exception during macro expansion: java.lang.NullPointerException at Impls$.foo3(Impls_1.scala:8) -#partest java20+ +#partest java20 at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) +#partest java21+ + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) +#partest java20+ at java.base/java.lang.reflect.Method.invoke(Method.java:578) at scala.reflect.macros.runtime.JavaReflectionRuntimes$JavaReflectionResolvers.$anonfun$resolveJavaReflectionRuntime$6(JavaReflectionRuntimes.scala:51) at scala.tools.nsc.typechecker.Macros.macroExpandWithRuntime(Macros.scala:849) diff --git a/test/files/run/sammy_after_implicit_view.scala b/test/files/run/sammy_after_implicit_view.scala index a13a71e56232..aa9158d0af7d 100644 --- a/test/files/run/sammy_after_implicit_view.scala +++ b/test/files/run/sammy_after_implicit_view.scala @@ -3,7 +3,7 @@ trait MySam { def apply(x: Int): String } // check that SAM conversion happens after implicit view application object Test extends App { final val AnonFunClass = "$anon$" - final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this + final val LMFClass = "$$Lambda" // LambdaMetaFactory names classes like this // if there's an implicit conversion, it does not takes precedence (because that's what dotty does) def implicitSam() = { diff --git a/test/files/run/sammy_restrictions_LMF.scala b/test/files/run/sammy_restrictions_LMF.scala index aa49e1411339..e8630beea1a6 100644 --- a/test/files/run/sammy_restrictions_LMF.scala +++ b/test/files/run/sammy_restrictions_LMF.scala @@ -15,7 +15,7 @@ trait TClassParent extends B { def apply(x: Int): String } object Test extends App { final val AnonFunClass = "$anonfun$" - final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this + final val LMFClass = "$$Lambda" // LambdaMetaFactory names classes like this private def LMF(f: Any): Unit = { val className = f.getClass.toString diff --git a/versions.properties b/versions.properties index 13334c1494fd..4eb37fb983c6 100644 --- a/versions.properties +++ b/versions.properties @@ -1,5 +1,5 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.12.18-M1 +starr.version=2.12.18-M2 # The scala.binary.version determines how modules are resolved. It is set as follows: # - After 2.x.0 is released, the binary version is 2.x From 66b03ce28a668ca491912ca67d70cd995d04f1fb Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 17 May 2023 19:16:48 -0700 Subject: [PATCH 251/261] sbt 1.8.3 (was .2) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 46e43a97ed86..72413de151e1 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.2 +sbt.version=1.8.3 From 1dfdedbe89926ef8f709440f316cb58d4953c420 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 17 May 2023 19:16:56 -0700 Subject: [PATCH 252/261] make some tests pass on JDK 21 --- test/files/run/sammy_after_implicit_view.scala | 2 +- test/files/run/sammy_restrictions_LMF.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/files/run/sammy_after_implicit_view.scala b/test/files/run/sammy_after_implicit_view.scala index 4d98754c48df..4d3d5a37cfd0 100644 --- a/test/files/run/sammy_after_implicit_view.scala +++ b/test/files/run/sammy_after_implicit_view.scala @@ -3,7 +3,7 @@ trait MySam { def apply(x: Int): String } // check that SAM conversion happens after implicit view application object Test extends App { final val AnonFunClass = "$anon$" - final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this + final val LMFClass = "$$Lambda" // LambdaMetaFactory names classes like this // if there's an implicit conversion, it does not takes precedence (because that's what dotty does) def implicitSam() = { diff --git a/test/files/run/sammy_restrictions_LMF.scala b/test/files/run/sammy_restrictions_LMF.scala index aa49e1411339..e8630beea1a6 100644 --- a/test/files/run/sammy_restrictions_LMF.scala +++ b/test/files/run/sammy_restrictions_LMF.scala @@ -15,7 +15,7 @@ trait TClassParent extends B { def apply(x: Int): String } object Test extends App { final val AnonFunClass = "$anonfun$" - final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this + final val LMFClass = "$$Lambda" // LambdaMetaFactory names classes like this private def LMF(f: Any): Unit = { val className = f.getClass.toString From 7808770a6f53c14508aaecbbba8c6498bc6095df Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 18 May 2023 10:51:58 -0700 Subject: [PATCH 253/261] fix a warning on JDK 21 --- test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java b/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java index eec35cef5a0e..e128f87fd6ce 100644 --- a/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java +++ b/test/junit/scala/jdk/javaapi/StreamConvertersJavaTest.java @@ -29,7 +29,7 @@ public class StreamConvertersJavaTest { public List li = CollectionConverters.asScala(Arrays.asList(1, 2)).toList(); public List lf = CollectionConverters.asScala(Arrays.asList(1f, 2f)).toList(); - public Map mkm(K k1, V v1, K k2, V v2) { + public static Map mkm(K k1, V v1, K k2, V v2) { java.util.Map m = new java.util.HashMap(); m.put(k1, v1); m.put(k2, v2); From 826dd7adec680322821b60d68984d0d819be6d24 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 18 May 2023 10:56:18 -0700 Subject: [PATCH 254/261] for now, skip TASTy tests on JDK 21 they won't pass until we're using a Scala 3 release that has a fix for scala/bug#12783 --- test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala index 0ce6bfbffe48..9a6470fb9c7d 100644 --- a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala +++ b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala @@ -2,6 +2,7 @@ package scala.tools.tastytest import org.junit.{Test => test, BeforeClass => setup, AfterClass => teardown} import org.junit.Assert._ +import org.junit.Assume._ import scala.util.{Try, Failure, Properties} @@ -91,6 +92,8 @@ object TastyTestJUnit { @setup def init(): Unit = { + // TODO remove once Scala 3 has a fix for scala/bug#12783 + assumeFalse(scala.util.Properties.isJavaAtLeast("21")) _dottyClassLoader = Dotc.initClassloader().get } From 38ace4cfbe85f45a2583a2fbcc671f5e2816b5e5 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Tue, 23 May 2023 09:53:23 -1000 Subject: [PATCH 255/261] Scala 3.3.0 (was -RC6) --- project/DottySupport.scala | 4 ++-- src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index b27b3a445ddd..a2ec7c9d0d87 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.3.0-RC6" // TASTy version 28.4-1 + val supportedTASTyRelease = "3.3.0" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.3.0-RC6" + val dottyVersion = "3.3.0" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( diff --git a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala index da7325eba4d9..546cdc15e23c 100644 --- a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala +++ b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala @@ -53,12 +53,7 @@ class TastyHeaderUnpickler(reader: TastyReader) { compilerExperimental = ExperimentalVersion ) - // TODO: remove this exception once we have a 3.3.0 - val dottyRCexception = validVersion || - (fileMajor == 28 && fileMinor == 3 && fileExperimental == 1 - && MajorVersion == 28 && MinorVersion == 3 && ExperimentalVersion == 0) - - check(dottyRCexception, { + check(validVersion, { val signature = signatureString(fileMajor, fileMinor, fileExperimental) val toolingVersion = new String(bytes, toolingStart.index, toolingLength) val producedByAddendum = s"\nThe TASTy file was produced by $toolingVersion.$toolingAddendum" From 2ec933d92a490b06d3554c7f6379af37c1644a1b Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Wed, 24 May 2023 11:24:41 +0200 Subject: [PATCH 256/261] docs: add in a note about elidable not being supported in Scala 3 This is a follow-up of the comment in https://github.com/lampepfl/dotty/issues/15766#issuecomment-1492846839 to try and make it clear to Scala 3 users that while this API exists and is visible in Scala 3, it's not supported by the actual compiler. Therefore it's recommended to instead use the `inline if` alternative. This just adds a note to the Scaladoc to that effect. refs: https://github.com/lampepfl/dotty/issues/15766 --- src/library/scala/annotation/elidable.scala | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/library/scala/annotation/elidable.scala b/src/library/scala/annotation/elidable.scala index 9d15449fac18..f2028bfd02fb 100644 --- a/src/library/scala/annotation/elidable.scala +++ b/src/library/scala/annotation/elidable.scala @@ -75,6 +75,29 @@ package scala.annotation * (O: C).f() // elided if compiled with `-Xelide-below 1` * } * }}} + * + * Note for Scala 3 users: + * If you're using Scala 3, the annotation exists since Scala 3 uses the Scala 2 + * standard library, but it's unsupported by the Scala 3 compiler. Instead, to + * achieve the same result you'd want to utilize the `inline if` feature to + * introduce behavior that makes a method de facto elided at compile-time. + * {{{ + * type LogLevel = Int + * + * object LogLevel: + * inline val Info = 0 + * inline val Warn = 1 + * inline val Debug = 2 + * + * inline val appLogLevel = LogLevel.Warn + * + * inline def log(msg: String, inline level: LogLevel): Unit = + * inline if (level <= appLogLevel) then println(msg) + * + * log("Warn log", LogLevel.Warn) + * + * log("Debug log", LogLevel. Debug) + * }}} */ final class elidable(final val level: Int) extends scala.annotation.ConstantAnnotation From 124dbff41d101e9d869a69dd89e2beaf7e2848c2 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Tue, 20 Sep 2022 15:49:05 +0200 Subject: [PATCH 257/261] bugfix: Matcher should be able to match on name exactly Previously, we would get completion candidates that would by filter by a wrong name if it was an alias (name of the type that was liased). Now, we find dealiased candidates later on, so no need to add the alias info. --- .../scala/tools/nsc/interactive/Global.scala | 30 ++++--------------- .../interpreter/PresentationCompilation.scala | 11 ++++--- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 9fe850139525..9eecc104ed05 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -992,7 +992,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") private[interactive] def getScopeCompletion(pos: Position, response: Response[List[Member]]): Unit = { informIDE("getScopeCompletion" + pos) - respond(response) { scopeMemberFlatten(scopeMembers(pos)) } + respond(response) { scopeMembers(pos) } } @nowarn("msg=inheritance from class LinkedHashMap") @@ -1045,14 +1045,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") // imported val and var are always marked as inaccessible, but they could be accessed through their getters. scala/bug#7995 val member = if (s.hasGetter) ScopeMember(s, st, context.isAccessible(s.getter, pre, superAccess = false), viaImport) - else { - if (s.isAliasType) { - val aliasInfo = ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) - ScopeMember(s.info.typeSymbol, s.info.typeSymbol.tpe, - context.isAccessible(s.info.typeSymbol, pre, superAccess = false), viaImport, - aliasInfo = Some(aliasInfo)) - } else ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) - } + else ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) member.prefix = pre member } @@ -1197,13 +1190,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") symbol.name.isTermName == name.isTermName || // Keep names of the same type name.isTypeName && isStable // Completing a type: keep stable terms (paths) } - // scala/bug#11846 aliasInfo should be match - def aliasTypeOk: Boolean = { - matcher(member.aliasInfo.map(_.sym.name).getOrElse(NoSymbol.name)) && !forImport && symbol.name.isTermName == name.isTermName - } - - !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || (matcher(member.sym.name) || aliasTypeOk) - && nameTypeOk) + !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || matcher(member.sym.name) && nameTypeOk) } } @@ -1224,11 +1211,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } - private def scopeMemberFlatten(members: List[ScopeMember]): List[ScopeMember] = { - val (infoWithoutAlias, infoWithAlias) = members.partition(_.aliasInfo.isEmpty) - infoWithoutAlias ++ infoWithAlias ++ infoWithAlias.flatten(_.aliasInfo) - } - final def completionsAt(pos: Position): CompletionResult = { val focus1: Tree = typedTreeAt(pos) def typeCompletions(tree: Tree, qual: Tree, nameStart: Int, name: Name): CompletionResult = { @@ -1256,13 +1238,13 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") val allMembers = scopeMembers(pos) val positionDelta: Int = pos.start - focus1.pos.start val subName = name.subName(0, positionDelta) - CompletionResult.ScopeMembers(positionDelta, scopeMemberFlatten(allMembers), subName, forImport = false) + CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = false) case imp@Import(i @ Ident(name), head :: Nil) if head.name == nme.ERROR => val allMembers = scopeMembers(pos) val nameStart = i.pos.start val positionDelta: Int = pos.start - nameStart val subName = name.subName(0, pos.start - i.pos.start) - CompletionResult.ScopeMembers(positionDelta, scopeMemberFlatten(allMembers), subName, forImport = true) + CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = true) case imp@Import(qual, selectors) => selectors.reverseIterator.find(_.namePos <= pos.start) match { case None => CompletionResult.NoResults @@ -1285,7 +1267,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") val allMembers = scopeMembers(pos) val positionDelta: Int = pos.start - focus1.pos.start val subName = name.subName(0, positionDelta) - CompletionResult.ScopeMembers(positionDelta, scopeMemberFlatten(allMembers), subName, forImport = false) + CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = false) case _ => CompletionResult.NoResults } diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index 97170236dc7c..8605d9f5163d 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -216,7 +216,12 @@ trait PresentationCompilation { self: IMain => val ccs = for { member <- matching if seen.add(member.sym) - sym <- if (member.sym.isClass && isNew) member.sym.info.decl(nme.CONSTRUCTOR).alternatives else member.sym.alternatives + candidate <- if (member.sym.isAliasType) { + val dealiased = member.sym.info.dealias.typeSymbol + seen.add(dealiased) + List(member.sym, dealiased) + } else List(member.sym) + sym <- if (candidate.isClass && isNew) candidate.info.decl(nme.CONSTRUCTOR).alternatives else candidate.alternatives sugared = sym.sugaredSymbolOrSelf } yield { CompletionCandidate( @@ -232,9 +237,7 @@ trait PresentationCompilation { self: IMain => val methodOtherDesc = if (!desc.exists(_ != "")) "" else " " + desc.filter(_ != "").mkString(" ") sugared.defStringSeenAs(tp) + methodOtherDesc } - }, - alias = member.aliasInfo.fold[Option[String]](None)(s => Some(s.sym.nameString)) - ) + }) } ccs } From f125f82a14b98895ce8b73980d7d0972d8d5436f Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 25 May 2023 10:13:43 -1000 Subject: [PATCH 258/261] slight tweak to completion logic, bringing code in line with intention Co-authored-by: Lukas Rytz --- src/interactive/scala/tools/nsc/interactive/Global.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 9eecc104ed05..28324f814b2a 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -1190,7 +1190,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") symbol.name.isTermName == name.isTermName || // Keep names of the same type name.isTypeName && isStable // Completing a type: keep stable terms (paths) } - !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || matcher(member.sym.name) && nameTypeOk) + !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || matcher(member.sym.name)) && nameTypeOk } } From a521eac9fd9e93058b8b1c404bc7f290519bc43c Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 25 May 2023 10:17:55 -1000 Subject: [PATCH 259/261] remove unused field in CompletionCandidate --- .../scala/tools/nsc/interpreter/jline/Reader.scala | 2 +- src/repl/scala/tools/nsc/interpreter/Interface.scala | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala index 0bc37ec1f374..1da41f7ea644 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala @@ -350,7 +350,7 @@ class Completion(delegate: shell.Completion) extends shell.Completion with Compl } val parsedLineWord = parsedLine.word() - result.candidates.filter(c => c.name == parsedLineWord || c.alias.fold(false)(a => a == parsedLineWord)) match { + result.candidates.filter(_.name == parsedLineWord) match { case Nil => case exacts => val declStrings = exacts.map(_.declString()).filterNot(_ == "") diff --git a/src/repl/scala/tools/nsc/interpreter/Interface.scala b/src/repl/scala/tools/nsc/interpreter/Interface.scala index efd1ed7487c8..89c8ad9daa2a 100644 --- a/src/repl/scala/tools/nsc/interpreter/Interface.scala +++ b/src/repl/scala/tools/nsc/interpreter/Interface.scala @@ -336,7 +336,6 @@ case class CompletionCandidate( isDeprecated: Boolean = false, isUniversal: Boolean = false, declString: () => String = () => "", - alias: Option[String] = None ) object CompletionCandidate { sealed trait Arity From 6a5318d11c4443129b93ab4c20c2582e16a90457 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 25 May 2023 10:58:44 -1000 Subject: [PATCH 260/261] Scala 3.3.1-RC1 (enable TastyTest on JDK 21) this should work on JDK 21 now that lampepfl/dotty#17536 has been merged --- project/DottySupport.scala | 4 ++-- test/tasty/neg/src-2/TestErasedTypes.check | 8 +------- test/tasty/neg/src-2/TestSaferExceptions.check | 2 +- .../tasty/test/scala/tools/tastytest/TastyTestJUnit.scala | 2 -- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index a2ec7c9d0d87..929b5ac73ddc 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.3.0" // TASTy version 28.4-1 + val supportedTASTyRelease = "3.3.1-RC1" // TASTy version 28.4-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease @@ -26,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.3.0" + val dottyVersion = "3.3.1-RC1" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).exists(_.toBoolean) lazy val commonSettings = Seq( diff --git a/test/tasty/neg/src-2/TestErasedTypes.check b/test/tasty/neg/src-2/TestErasedTypes.check index 36e8612ded99..9db7d1c7e747 100644 --- a/test/tasty/neg/src-2/TestErasedTypes.check +++ b/test/tasty/neg/src-2/TestErasedTypes.check @@ -1,9 +1,3 @@ -TestErasedTypes_fail.scala:6: error: Unsupported Scala 3 erased modifier in refinement of type F; found in class tastytest.ErasedTypes.Bar. - def test1 = new Bar[Foo] - ^ -TestErasedTypes_fail.scala:7: error: Unsupported Scala 3 erased modifier in refinement of type F; found in class tastytest.ErasedTypes.Baz. - def test2 = new Baz[Foo] - ^ TestErasedTypes_fail.scala:9: error: Unsupported Scala 3 erased value x; found in method foo1 in trait tastytest.ErasedTypes.Foo. def test3(f: Foo) = f.foo1("foo") ^ @@ -13,4 +7,4 @@ TestErasedTypes_fail.scala:10: error: Unsupported Scala 3 erased value x; found TestErasedTypes_fail.scala:12: error: Unsupported Scala 3 erased method theString; found in object tastytest.ErasedTypes.ErasedCompileTimeOps. def test5 = ErasedCompileTimeOps.theString ^ -5 errors +3 errors diff --git a/test/tasty/neg/src-2/TestSaferExceptions.check b/test/tasty/neg/src-2/TestSaferExceptions.check index ad1ae4edd7d8..6090209bcb5d 100644 --- a/test/tasty/neg/src-2/TestSaferExceptions.check +++ b/test/tasty/neg/src-2/TestSaferExceptions.check @@ -1,4 +1,4 @@ -TestSaferExceptions_fail.scala:5: error: Unsupported Scala 3 erased context function type in bounds of type mayThrow: erased tastytest.SaferExceptions.CanThrowCapability[E] ?=> A; found in object tastytest.SaferExceptions. +TestSaferExceptions_fail.scala:5: error: Unsupported Scala 3 erased value x$1; found in method apply in tastytest.SaferExceptions.. def test = SaferExceptions.safeDiv(1, 0) // error ^ 1 error diff --git a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala index 9a6470fb9c7d..0eb353a54cd7 100644 --- a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala +++ b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala @@ -92,8 +92,6 @@ object TastyTestJUnit { @setup def init(): Unit = { - // TODO remove once Scala 3 has a fix for scala/bug#12783 - assumeFalse(scala.util.Properties.isJavaAtLeast("21")) _dottyClassLoader = Dotc.initClassloader().get } From 3fdd09135aa89f6d3cfa51a7655c95df412c664e Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Wed, 31 May 2023 15:40:25 -0700 Subject: [PATCH 261/261] revert pull request #10148 in its entirety to be revisited after 2.13.11 --- .../scala/tools/nsc/interactive/Global.scala | 30 +++++++++++++++---- .../tools/nsc/interpreter/jline/Reader.scala | 2 +- .../tools/nsc/interpreter/Interface.scala | 1 + .../interpreter/PresentationCompilation.scala | 11 +++---- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 28324f814b2a..9fe850139525 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -992,7 +992,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") private[interactive] def getScopeCompletion(pos: Position, response: Response[List[Member]]): Unit = { informIDE("getScopeCompletion" + pos) - respond(response) { scopeMembers(pos) } + respond(response) { scopeMemberFlatten(scopeMembers(pos)) } } @nowarn("msg=inheritance from class LinkedHashMap") @@ -1045,7 +1045,14 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") // imported val and var are always marked as inaccessible, but they could be accessed through their getters. scala/bug#7995 val member = if (s.hasGetter) ScopeMember(s, st, context.isAccessible(s.getter, pre, superAccess = false), viaImport) - else ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) + else { + if (s.isAliasType) { + val aliasInfo = ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) + ScopeMember(s.info.typeSymbol, s.info.typeSymbol.tpe, + context.isAccessible(s.info.typeSymbol, pre, superAccess = false), viaImport, + aliasInfo = Some(aliasInfo)) + } else ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) + } member.prefix = pre member } @@ -1190,7 +1197,13 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") symbol.name.isTermName == name.isTermName || // Keep names of the same type name.isTypeName && isStable // Completing a type: keep stable terms (paths) } - !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || matcher(member.sym.name)) && nameTypeOk + // scala/bug#11846 aliasInfo should be match + def aliasTypeOk: Boolean = { + matcher(member.aliasInfo.map(_.sym.name).getOrElse(NoSymbol.name)) && !forImport && symbol.name.isTermName == name.isTermName + } + + !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || (matcher(member.sym.name) || aliasTypeOk) + && nameTypeOk) } } @@ -1211,6 +1224,11 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } + private def scopeMemberFlatten(members: List[ScopeMember]): List[ScopeMember] = { + val (infoWithoutAlias, infoWithAlias) = members.partition(_.aliasInfo.isEmpty) + infoWithoutAlias ++ infoWithAlias ++ infoWithAlias.flatten(_.aliasInfo) + } + final def completionsAt(pos: Position): CompletionResult = { val focus1: Tree = typedTreeAt(pos) def typeCompletions(tree: Tree, qual: Tree, nameStart: Int, name: Name): CompletionResult = { @@ -1238,13 +1256,13 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") val allMembers = scopeMembers(pos) val positionDelta: Int = pos.start - focus1.pos.start val subName = name.subName(0, positionDelta) - CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = false) + CompletionResult.ScopeMembers(positionDelta, scopeMemberFlatten(allMembers), subName, forImport = false) case imp@Import(i @ Ident(name), head :: Nil) if head.name == nme.ERROR => val allMembers = scopeMembers(pos) val nameStart = i.pos.start val positionDelta: Int = pos.start - nameStart val subName = name.subName(0, pos.start - i.pos.start) - CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = true) + CompletionResult.ScopeMembers(positionDelta, scopeMemberFlatten(allMembers), subName, forImport = true) case imp@Import(qual, selectors) => selectors.reverseIterator.find(_.namePos <= pos.start) match { case None => CompletionResult.NoResults @@ -1267,7 +1285,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") val allMembers = scopeMembers(pos) val positionDelta: Int = pos.start - focus1.pos.start val subName = name.subName(0, positionDelta) - CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = false) + CompletionResult.ScopeMembers(positionDelta, scopeMemberFlatten(allMembers), subName, forImport = false) case _ => CompletionResult.NoResults } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala index 1da41f7ea644..0bc37ec1f374 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala @@ -350,7 +350,7 @@ class Completion(delegate: shell.Completion) extends shell.Completion with Compl } val parsedLineWord = parsedLine.word() - result.candidates.filter(_.name == parsedLineWord) match { + result.candidates.filter(c => c.name == parsedLineWord || c.alias.fold(false)(a => a == parsedLineWord)) match { case Nil => case exacts => val declStrings = exacts.map(_.declString()).filterNot(_ == "") diff --git a/src/repl/scala/tools/nsc/interpreter/Interface.scala b/src/repl/scala/tools/nsc/interpreter/Interface.scala index 89c8ad9daa2a..efd1ed7487c8 100644 --- a/src/repl/scala/tools/nsc/interpreter/Interface.scala +++ b/src/repl/scala/tools/nsc/interpreter/Interface.scala @@ -336,6 +336,7 @@ case class CompletionCandidate( isDeprecated: Boolean = false, isUniversal: Boolean = false, declString: () => String = () => "", + alias: Option[String] = None ) object CompletionCandidate { sealed trait Arity diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index 8605d9f5163d..97170236dc7c 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -216,12 +216,7 @@ trait PresentationCompilation { self: IMain => val ccs = for { member <- matching if seen.add(member.sym) - candidate <- if (member.sym.isAliasType) { - val dealiased = member.sym.info.dealias.typeSymbol - seen.add(dealiased) - List(member.sym, dealiased) - } else List(member.sym) - sym <- if (candidate.isClass && isNew) candidate.info.decl(nme.CONSTRUCTOR).alternatives else candidate.alternatives + sym <- if (member.sym.isClass && isNew) member.sym.info.decl(nme.CONSTRUCTOR).alternatives else member.sym.alternatives sugared = sym.sugaredSymbolOrSelf } yield { CompletionCandidate( @@ -237,7 +232,9 @@ trait PresentationCompilation { self: IMain => val methodOtherDesc = if (!desc.exists(_ != "")) "" else " " + desc.filter(_ != "").mkString(" ") sugared.defStringSeenAs(tp) + methodOtherDesc } - }) + }, + alias = member.aliasInfo.fold[Option[String]](None)(s => Some(s.sym.nameString)) + ) } ccs }