From 8dfbd6d36b35c202a67ad78b72a9b23f24f11fd6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 16 Aug 2023 11:57:00 +0200 Subject: [PATCH 001/134] Improve non-static macro implementation error message If non-static inline accessor is generated we do not we can tell the user why they cannot access the macro implementation this way. Currently we do not have a clean way to fix this code, but in the future [SIP-58](https://github.com/scala/improvement-proposals/pull/58) would introduce a way to not generate this accessor. Fixes #15413 [Cherry-picked f1db208c426f42414a57c180dbe68090161787cb] --- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 8 ++++++++ tests/neg-macros/i15413.check | 6 ++++++ tests/neg-macros/i15413.scala | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 tests/neg-macros/i15413.check create mode 100644 tests/neg-macros/i15413.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index c870336995f0..07f6b823bf8f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -31,6 +31,7 @@ import dotty.tools.dotc.quoted.{PickledQuotes, QuoteUtils} import scala.quoted.Quotes import scala.quoted.runtime.impl._ +import dotty.tools.dotc.core.NameKinds /** Utility class to splice quoted expressions */ object Splicer { @@ -214,6 +215,13 @@ object Splicer { report.error("Macro cannot be implemented with an `inline` method", fn.srcPos) args.flatten.foreach(checkIfValidArgument) + case Call(fn, args) if fn.symbol.name.is(NameKinds.InlineAccessorName) => + // TODO suggest use of @binaryAPI one we have the annotation + report.error( + i"""Macro implementation is not statically accessible. + | + |Non-static inline accessor was generated in ${fn.symbol.owner} + |""".stripMargin, tree.srcPos) case _ => report.error( """Malformed macro. diff --git a/tests/neg-macros/i15413.check b/tests/neg-macros/i15413.check new file mode 100644 index 000000000000..56f587eb2fb4 --- /dev/null +++ b/tests/neg-macros/i15413.check @@ -0,0 +1,6 @@ +-- Error: tests/neg-macros/i15413.scala:4:22 --------------------------------------------------------------------------- +4 | inline def foo = ${ Macro.fooImpl } // error + | ^^^^^^^^^^^^^ + | Macro implementation is not statically accessible. + | + | Non-static inline accessor was generated in class Macro diff --git a/tests/neg-macros/i15413.scala b/tests/neg-macros/i15413.scala new file mode 100644 index 000000000000..186ba60f3d25 --- /dev/null +++ b/tests/neg-macros/i15413.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +class Macro: + inline def foo = ${ Macro.fooImpl } // error + +object Macro: + private def fooImpl(using Quotes) = '{} From d5bdc1650847ff38b3985b926bc8e3764b0aa29a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 17 Aug 2023 20:23:58 +0200 Subject: [PATCH 002/134] Update compiler/src/dotty/tools/dotc/transform/Splicer.scala Co-authored-by: Jan Chyb <48855024+jchyb@users.noreply.github.com> [Cherry-picked 546ecf946ba21228b3bbb4a5334ad416dfddcfdd] --- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 07f6b823bf8f..46a8571b4f30 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -216,7 +216,7 @@ object Splicer { args.flatten.foreach(checkIfValidArgument) case Call(fn, args) if fn.symbol.name.is(NameKinds.InlineAccessorName) => - // TODO suggest use of @binaryAPI one we have the annotation + // TODO suggest use of @binaryAPI once we have the annotation report.error( i"""Macro implementation is not statically accessible. | From dd1851c863f03b61e52718fcfadda648f325f3a4 Mon Sep 17 00:00:00 2001 From: Florian3k Date: Mon, 28 Aug 2023 15:07:50 +0200 Subject: [PATCH 003/134] scaladoc: fix link resolving [Cherry-picked 12b4e0ec02556520f78984170d8e119384bee608] --- .../dotty/tools/scaladoc/renderers/SiteRenderer.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala index 9e464da893b3..7f64ce92ffc8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala @@ -29,6 +29,14 @@ trait SiteRenderer(using DocContext) extends Locations: def siteContent(pageDri: DRI, content: ResolvedTemplate): PageContent = import content.ctx + + def tryAsDriPlain(str: String): Option[String] = + val (path, prefix) = str match + case HashRegex(path, prefix) => (path, prefix) + case _ => (str, "") + val res = ctx.driForLink(content.template.file, path).filter(driExists) + res.headOption.map(pathToPage(pageDri, _) + prefix) + def tryAsDri(str: String): Option[String] = val newStr = str.dropWhile(c => c == '.' || c == '/').replaceAll("/", ".") match @@ -51,7 +59,7 @@ trait SiteRenderer(using DocContext) extends Locations: )( resolveLink(pageDri, str.stripPrefix("/")) ) - def asStaticSite: Option[String] = tryAsDri(str) + def asStaticSite: Option[String] = tryAsDriPlain(str).orElse(tryAsDri(str)) /* Link resolving checks performs multiple strategies with following priority: 1. We check if the link is a valid URL e.g. http://dotty.epfl.ch From ede0d3d019f20de4194468ac5f418af9a33ab46f Mon Sep 17 00:00:00 2001 From: Florian3k Date: Mon, 28 Aug 2023 15:12:19 +0200 Subject: [PATCH 004/134] docs: fix invalid links [Cherry-picked b79908b09f573b0337ab1f4f141af2a44f4944c1] --- docs/_docs/contributing/architecture/types.md | 2 +- docs/_docs/contributing/debugging/debugging.md | 2 +- docs/_docs/contributing/issues/cause.md | 2 +- docs/_docs/contributing/issues/reproduce.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_docs/contributing/architecture/types.md b/docs/_docs/contributing/architecture/types.md index 64543e555e69..2dfdc33101a0 100644 --- a/docs/_docs/contributing/architecture/types.md +++ b/docs/_docs/contributing/architecture/types.md @@ -11,7 +11,7 @@ format corresponding to the backing data structure, e.g. `ExprType(...)` corresponds to `class ExprType`, defined in [Types.scala]. > You can inspect the representation of any type using the [dotty.tools.printTypes][DottyTypeStealer] -> script, its usage and integration into your debugging workflow is [described here](../issues/inspection.md). +> script, its usage and integration into your debugging workflow is [described here](../debugging/inspection.md). ### Types of Definitions diff --git a/docs/_docs/contributing/debugging/debugging.md b/docs/_docs/contributing/debugging/debugging.md index 5035620a45aa..a96e6bcdc7db 100644 --- a/docs/_docs/contributing/debugging/debugging.md +++ b/docs/_docs/contributing/debugging/debugging.md @@ -9,6 +9,6 @@ that you're having issues with. This can be just inspecting the trees of your code or stepping through the dotty codebase with a Debugger. The following sections will help you with this: -- [Debugging with your IDE](./ide-debugging.md.md) +- [Debugging with your IDE](./ide-debugging.md) - [How to Inspect Values](./inspection.md) - [Other Debugging Techniques](./other-debugging.md) diff --git a/docs/_docs/contributing/issues/cause.md b/docs/_docs/contributing/issues/cause.md index 385c72dc8738..e23f6d1f747f 100644 --- a/docs/_docs/contributing/issues/cause.md +++ b/docs/_docs/contributing/issues/cause.md @@ -69,7 +69,7 @@ This flag forces a stack trace to be printed each time an error happens, from th Analysing the trace will give you a clue about the objects involved in producing the error. For example, you can add some debug statements before the error is issued to discover the state of the compiler. [See some useful ways to debug -values.](./debugging/inspection.md) +values.](../debugging/inspection.md) ### Where was a particular object created? diff --git a/docs/_docs/contributing/issues/reproduce.md b/docs/_docs/contributing/issues/reproduce.md index 7d04e6794ed2..ca5da324a867 100644 --- a/docs/_docs/contributing/issues/reproduce.md +++ b/docs/_docs/contributing/issues/reproduce.md @@ -133,7 +133,7 @@ not be passed). This saves typing the same arguments each time you want to use t The following `launch.iss` file is an example of how you can use multiline commands as a template for solving issues that [run compiled -code](../issues/testing.md#checking-program-output). It demonstrates configuring +code](../testing.md#checking-program-output). It demonstrates configuring the `scala3/scalac` command using compiler flags, which are commented out. Put your favourite flags there for quick usage. From 21ff13004810cbfe0d8904d0b9e4d32ac576a4f4 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Tue, 19 Sep 2023 15:32:37 +0200 Subject: [PATCH 005/134] Avoid flattening `List` of `List`s [Cherry-picked c39d260042332803259563110603521ed6381db6] --- .../tools/dotc/transform/CheckUnused.scala | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 0eeec0f3cbec..eb3c37622e0d 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -561,11 +561,19 @@ object CheckUnused: else Nil val warnings = - List(sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, - sortedPrivateDefs, sortedPatVars, unsetLocalDefs, unsetPrivateDefs).flatten.sortBy { s => - val pos = s.pos.sourcePos - (pos.line, pos.column) - } + val unsorted = + sortedImp ::: + sortedLocalDefs ::: + sortedExplicitParams ::: + sortedImplicitParams ::: + sortedPrivateDefs ::: + sortedPatVars ::: + unsetLocalDefs ::: + unsetPrivateDefs + unsorted.sortBy { s => + val pos = s.pos.sourcePos + (pos.line, pos.column) + } UnusedResult(warnings.toSet) end getUnused //============================ HELPERS ==================================== From 5a40ae6ec7b17a54bfe8b9aba571c2429e53edc3 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Tue, 19 Sep 2023 15:44:14 +0200 Subject: [PATCH 006/134] Remove redundant `.toList` call on a `List` Co-authored-by: Nicolas Stucki [Cherry-picked 0c04c2e5206e8e5ade2cc8c31efb394803e62e28] --- compiler/src/dotty/tools/MainGenericRunner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 6f4366a00b77..0a5a1b191a44 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -148,7 +148,7 @@ object MainGenericRunner { case (o @ javaOption(striped)) :: tail => processArgs(tail, settings.withJavaArgs(striped).withScalaArgs(o)) case (o @ scalaOption(_*)) :: tail => - val remainingArgs = (CommandLineParser.expandArg(o) ++ tail).toList + val remainingArgs = CommandLineParser.expandArg(o) ++ tail processArgs(remainingArgs, settings) case (o @ colorOption(_*)) :: tail => processArgs(tail, settings.withScalaArgs(o)) From e5ed578999f2cf975022ac068be6475bc1ee7c02 Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Tue, 19 Sep 2023 15:48:18 +0200 Subject: [PATCH 007/134] Avoid creating a List to initialize fields [Cherry-picked c3377d4900cba307d291fc56e39187431fc9aa7a] --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 5e4b854fc9e2..c932f528404b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -2119,8 +2119,10 @@ class Definitions { this.initCtx = ctx if (!isInitialized) { // force initialization of every symbol that is synthesized or hijacked by the compiler - val forced = - syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass + syntheticCoreClasses + syntheticCoreMethods + JavaEnumClass + // end force initialization isInitialized = true } addSyntheticSymbolsComments From e91fddf09e65d98ae58694d8ff2bb5893cfcbf0c Mon Sep 17 00:00:00 2001 From: Lorenzo Gabriele Date: Wed, 20 Sep 2023 12:01:46 +0200 Subject: [PATCH 008/134] Add back `ScalaValueClasses()` initialization The call to `apply` perform side effects that need to be executed [Cherry-picked 240e95ae148189c46ce990d0d71db968f7cffe63] --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index c932f528404b..cebc2cb67c45 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -2121,6 +2121,7 @@ class Definitions { // force initialization of every symbol that is synthesized or hijacked by the compiler syntheticCoreClasses syntheticCoreMethods + ScalaValueClasses() JavaEnumClass // end force initialization isInitialized = true From 1834d12f89e3bbfeef423374e6f0dc28215c85fc Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 22 Sep 2023 13:09:59 +0200 Subject: [PATCH 009/134] [spec] some fixes to tuples [Cherry-picked a2bbef0f2a6514982492da01ef20e3ed7ce32436] --- docs/_spec/03-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 1cf58b789182..407a69b8c8c5 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -239,13 +239,13 @@ SimpleType1 ::= ... | ‘(’ TypesOrWildcards ‘)’ ``` -A _tuple type_ ´(T_1, ..., T_n)´ where ´n \geq 2´ is sugar for the type `´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple`, which is itself a series of nested infix types which are sugar for `*:[´T_1´, *:[´T_2´, ... *[´T_n´, scala.EmptyTuple]]]`. +A _tuple type_ ´(T_1, ..., T_n)´ where ´n \geq 2´ is sugar for the type `´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple`, which is itself a series of nested infix types which are sugar for `*:[´T_1´, *:[´T_2´, ... *:[´T_n´, scala.EmptyTuple]]]`. The ´T_i´ can be wildcard type arguments. Notes: - `(´T_1´)` is the type ´T_1´, and not `´T_1´ *: scala.EmptyTuple` (´T_1´ cannot be a wildcard type argument in that case). -- `()` is not a valid type (not even `scala.EmptyTuple`). +- `()` is not a valid type (i.e. it is not desugared to `scala.EmptyTuple`). ### Concrete Refined Types From bd5555ab321a4394c8d907867dc2dcb330cc34a0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 21 Aug 2023 09:34:56 +0200 Subject: [PATCH 010/134] `LambdaType.derivedLambdaType` return a `LambdaType.This` [Cherry-picked aa2c851f2d75e9395a63b0868a0ee77f655ba293] --- compiler/src/dotty/tools/dotc/core/Types.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index fab9ef637801..0bae5ce07722 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3640,7 +3640,7 @@ object Types { trait LambdaType extends BindingType with TermType { self => type ThisName <: Name type PInfo <: Type - type This <: LambdaType{type PInfo = self.PInfo} + type This >: this.type <: LambdaType{type PInfo = self.PInfo} type ParamRefType <: ParamRef def paramNames: List[ThisName] @@ -3698,7 +3698,7 @@ object Types { final def derivedLambdaType(paramNames: List[ThisName] = this.paramNames, paramInfos: List[PInfo] = this.paramInfos, - resType: Type = this.resType)(using Context): LambdaType = + resType: Type = this.resType)(using Context): This = if ((paramNames eq this.paramNames) && (paramInfos eq this.paramInfos) && (resType eq this.resType)) this else newLikeThis(paramNames, paramInfos, resType) @@ -3817,7 +3817,7 @@ object Types { import DepStatus._ type ThisName = TermName type PInfo = Type - type This <: TermLambda + type This >: this.type <: TermLambda type ParamRefType = TermParamRef override def resultType(using Context): Type = @@ -4114,7 +4114,7 @@ object Types { trait TypeLambda extends LambdaType { type ThisName = TypeName type PInfo = TypeBounds - type This <: TypeLambda + type This >: this.type <: TypeLambda type ParamRefType = TypeParamRef def isResultDependent(using Context): Boolean = true From 6eaf2c905600d7a0b6c163c8df32931868b39f00 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 17 Aug 2023 23:56:57 +0100 Subject: [PATCH 011/134] Fix failing bounds check on default getter The type arguments on the default argument had the wrong span, which means that they were getting bounds checked. This is in contrast the other other type arguments (the ones on check itself), which aren't bounds checked - by not passing the isZeroExtent guard in checkInferredWellFormed. [Cherry-picked e8c8038548d90a54eb2d2a3bfe4a13700d70dcce] --- .../src/dotty/tools/dotc/typer/Applications.scala | 2 +- tests/pos/i18253.orig.scala | 15 +++++++++++++++ tests/pos/i18253.scala | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18253.orig.scala create mode 100644 tests/pos/i18253.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index eebb63590961..19af6b02c25d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -333,7 +333,7 @@ object Applications { // it's crucial that the type tree is not copied directly as argument to // `cpy$default$1`. If it was, the variable `X'` would already be interpolated // when typing the default argument, which is too early. - spliceMeth(meth, fn).appliedToTypes(targs.tpes) + spliceMeth(meth, fn).appliedToTypeTrees(targs.map(targ => TypeTree(targ.tpe).withSpan(targ.span))) case _ => meth } diff --git a/tests/pos/i18253.orig.scala b/tests/pos/i18253.orig.scala new file mode 100644 index 000000000000..9efe1224ebfd --- /dev/null +++ b/tests/pos/i18253.orig.scala @@ -0,0 +1,15 @@ +import compiletime.ops.int.Max + +trait DFSInt[W <: Int] +trait Candidate[R]: + type OutW <: Int +object Candidate: + given [W <: Int, R <: DFSInt[W]]: Candidate[R] with + type OutW = W + +def foo[R](rhs: R)(using icR: Candidate[R]): DFSInt[Max[8, icR.OutW]] = ??? + +object Test: + def check[A](a: A, clue: Int = 1): Unit = ??? + val x: DFSInt[8] = ??? + check(foo(x)) diff --git a/tests/pos/i18253.scala b/tests/pos/i18253.scala new file mode 100644 index 000000000000..8f395ee8e943 --- /dev/null +++ b/tests/pos/i18253.scala @@ -0,0 +1,14 @@ +import scala.compiletime.ops.int.Max + +trait Foo[A] +trait Bar[B]: + type Out <: Int +object Bar: + given inst[C <: Int]: Bar[C] with + type Out = C + +class Test: + def mkFoo(using bx: Bar[2]): Foo[Max[1, bx.Out]] = ??? + def check[Y](yy: Y, clue: Int = 1): Unit = () + + def test: Unit = check(mkFoo) From 357ade31b32ecb616663e63289d45a8565a4ef78 Mon Sep 17 00:00:00 2001 From: Jakub Ciesluk <323892@uwr.edu.pl> Date: Mon, 21 Aug 2023 09:51:10 +0200 Subject: [PATCH 012/134] fix: Don't collect map, flatMap, withFilter in for-comprehension [Cherry-picked 5554a4b970b3678c8437a0212e9443b495f6de49] --- .../src/main/dotty/tools/pc/PcCollector.scala | 15 +++- .../tools/pc/tests/edit/PcRenameSuite.scala | 13 +++ .../highlight/DocumentHighlightSuite.scala | 84 +++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala index 1700c4e16197..274b0704674d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala @@ -409,7 +409,9 @@ abstract class PcCollector[T]( * All select statements such as: * val a = hello.<> */ - case sel: Select if sel.span.isCorrect && filter(sel) => + case sel: Select + if sel.span.isCorrect && filter(sel) && + !isForComprehensionMethod(sel) => occurrences + collect( sel, pos.withSpan(selectNameSpan(sel)) @@ -560,6 +562,17 @@ abstract class PcCollector[T]( Span(span.start, span.start + realName.length, point) else Span(point, span.end, point) else span + + private val forCompMethods = + Set(nme.map, nme.flatMap, nme.withFilter, nme.foreach) + + // We don't want to collect synthethic `map`, `withFilter`, `foreach` and `flatMap` in for-comprenhensions + private def isForComprehensionMethod(sel: Select): Boolean = + val syntheticName = sel.name match + case name: TermName => forCompMethods(name) + case _ => false + val wrongSpan = sel.qualifier.span.contains(sel.nameSpan) + syntheticName && wrongSpan end PcCollector object PcCollector: diff --git a/presentation-compiler/test/dotty/tools/pc/tests/edit/PcRenameSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/edit/PcRenameSuite.scala index a6254ef3ac09..256b0cb1075a 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/edit/PcRenameSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/edit/PcRenameSuite.scala @@ -471,3 +471,16 @@ class PcRenameSuite extends BasePcRenameSuite: |} yield <> |""".stripMargin ) + + @Test def `map-method` = + check( + """|case class Bar(x: List[Int]) { + | def <>(f: Int => Int): Bar = Bar(x.map(f)) + |} + | + |val f = + | for { + | b <- Bar(List(1,2,3)) + | } yield b + |""".stripMargin, + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala index 47a07c873a61..4bf04ed1d0af 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala @@ -758,6 +758,90 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: | } |}""".stripMargin ) + + @Test def `for-comp-map` = + check( + """|object Main { + | val x = List(1).<>(_ + 1) + | val y = for { + | a <- List(1) + | } yield a + 1 + |} + |""".stripMargin, + ) + + @Test def `for-comp-map1` = + check( + """|object Main { + | val x = List(1).<>(_ + 1) + | val y = for { + | a <- List(1) + | if true + | } yield a + 1 + |} + |""".stripMargin, + ) + + @Test def `for-comp-foreach` = + check( + """|object Main { + | val x = List(1).<>(_ => ()) + | val y = for { + | a <- List(1) + | } {} + |} + |""".stripMargin, + ) + + @Test def `for-comp-withFilter` = + check( + """|object Main { + | val x = List(1).<>(_ => true) + | val y = for { + | a <- List(1) + | if true + | } {} + |} + |""".stripMargin, + ) + + @Test def `for-comp-withFilter1` = + check( + """|object Main { + | val x = List(1).withFilter(_ => true).<>(_ + 1) + | val y = for { + | a <- List(1) + | if true + | } yield a + 1 + |} + |""".stripMargin, + ) + + @Test def `for-comp-flatMap1` = + check( + """|object Main { + | val x = List(1).<>(_ => List(1)) + | val y = for { + | a <- List(1) + | b <- List(2) + | if true + | } yield a + 1 + |} + |""".stripMargin, + ) + + @Test def `for-comp-flatMap2` = + check( + """|object Main { + | val x = List(1).withFilter(_ => true).<>(_ => List(1)) + | val y = for { + | a <- List(1) + | if true + | b <- List(2) + | } yield a + 1 + |} + |""".stripMargin, + ) @Test def `enum1` = check( From 83729ec8c970c0fef2970e1bb3a17111908ca0b9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 28 Aug 2023 09:36:30 +0200 Subject: [PATCH 013/134] Count size of parameters for platform limit check Fixes #18458 [Cherry-picked 0f62db9c1444cec8d494725337a50c8cb205134c] --- .../tools/backend/jvm/BCodeSkelBuilder.scala | 9 +- tests/neg/i18458.check | 4 + tests/neg/i18458.scala | 206 ++++++++++++++++++ 3 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 tests/neg/i18458.check create mode 100644 tests/neg/i18458.scala diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 0a11fb898b48..f892c2bb753c 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -763,9 +763,14 @@ trait BCodeSkelBuilder extends BCodeHelpers { for (p <- params) { locals.makeLocal(p.symbol) } // debug assert((params.map(p => locals(p.symbol).tk)) == asmMethodType(methSymbol).getArgumentTypes.toList, "debug") - if (params.size > MaximumJvmParameters) { + val paramsSize = params.map { param => + val tpeTym = param.symbol.info.typeSymbol + if tpeTym == defn.LongClass || tpeTym == defn.DoubleClass then 2 else 1 + }.sum + if (paramsSize > MaximumJvmParameters) { // SI-7324 - report.error(em"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.", ctx.source.atSpan(methSymbol.span)) + val info = if paramsSize == params.length then "" else " (Long and Double count as 2)" // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3 + report.error(em"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters$info.", ctx.source.atSpan(methSymbol.span)) return } diff --git a/tests/neg/i18458.check b/tests/neg/i18458.check new file mode 100644 index 000000000000..5e8bb4e33121 --- /dev/null +++ b/tests/neg/i18458.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i18458.scala:1:4 ----------------------------------------------------------------------------------- +1 |def foo( // error + | ^ + | Platform restriction: a parameter list's length cannot exceed 254 (Long and Double count as 2). diff --git a/tests/neg/i18458.scala b/tests/neg/i18458.scala new file mode 100644 index 000000000000..a14286addc28 --- /dev/null +++ b/tests/neg/i18458.scala @@ -0,0 +1,206 @@ +def foo( // error + foo1: Long, + foo2: Long, + foo3: Long, + foo4: Long, + foo5: Long, + foo6: Long, + foo7: Long, + foo8: Long, + foo9: Long, + foo10: Long, + foo11: Long, + foo12: Long, + foo13: Long, + foo14: Long, + foo15: Long, + foo16: Long, + foo17: Long, + foo18: Long, + foo19: Long, + foo20: Long, + foo21: Long, + foo22: Long, + foo23: Long, + foo24: Long, + foo25: Long, + foo26: Long, + foo27: Long, + foo28: Long, + foo29: Long, + foo30: Long, + foo31: Long, + foo32: Long, + foo33: Long, + foo34: Long, + foo35: Long, + foo36: Long, + foo37: Long, + foo38: Long, + foo39: Long, + foo40: Long, + foo41: Long, + foo42: Long, + foo43: Long, + foo44: Long, + foo45: Long, + foo46: Long, + foo47: Long, + foo48: Long, + foo49: Long, + foo50: Long, + foo51: Long, + foo52: Long, + foo53: Long, + foo54: Long, + foo55: Long, + foo56: Long, + foo57: Long, + foo58: Long, + foo59: Long, + foo60: Long, + foo61: Long, + foo62: Long, + foo63: Long, + foo64: Long, + foo65: Long, + foo66: Long, + foo67: Long, + foo68: Long, + foo69: Long, + foo70: Long, + foo71: Long, + foo72: Long, + foo73: Long, + foo74: Long, + foo75: Long, + foo76: Long, + foo77: Long, + foo78: Long, + foo79: Long, + foo80: Long, + foo81: Long, + foo82: Long, + foo83: Long, + foo84: Long, + foo85: Long, + foo86: Long, + foo87: Long, + foo88: Long, + foo89: Long, + foo90: Long, + foo91: Long, + foo92: Long, + foo93: Long, + foo94: Long, + foo95: Long, + foo96: Long, + foo97: Long, + foo98: Long, + foo99: Long, + foo100: Long, + foo101: Long, + foo102: Long, + foo103: Long, + foo104: Long, + foo105: Long, + foo106: Long, + foo107: Long, + foo108: Long, + foo109: Long, + foo110: Long, + foo111: Long, + foo112: Long, + foo113: Long, + foo114: Long, + foo115: Long, + foo116: Long, + foo117: Long, + foo118: Long, + foo119: Long, + foo120: Long, + foo121: Long, + foo122: Long, + foo123: Long, + foo124: Long, + foo125: Long, + foo126: Long, + foo127: Long, + foo128: Long, + foo129: Long, + foo130: Long, + foo131: Long, + foo132: Long, + foo133: Long, + foo134: Long, + foo135: Long, + foo136: Long, + foo137: Long, + foo138: Long, + foo139: Long, + foo140: Long, + foo141: Long, + foo142: Long, + foo143: Long, + foo144: Long, + foo145: Long, + foo146: Long, + foo147: Long, + foo148: Long, + foo149: Long, + foo150: Long, + foo151: Long, + foo152: Long, + foo153: Long, + foo154: Long, + foo155: Long, + foo156: Long, + foo157: Long, + foo158: Long, + foo159: Long, + foo160: Long, + foo161: Long, + foo162: Long, + foo163: Long, + foo164: Long, + foo165: Long, + foo166: Long, + foo167: Long, + foo168: Long, + foo169: Long, + foo170: Long, + foo171: Long, + foo172: Long, + foo173: Long, + foo174: Long, + foo175: Long, + foo176: Long, + foo177: Long, + foo178: Long, + foo179: Long, + foo180: Long, + foo181: Long, + foo182: Long, + foo183: Long, + foo184: Long, + foo185: Long, + foo186: Long, + foo187: Long, + foo188: Long, + foo189: Long, + foo190: Long, + foo191: Long, + foo192: Long, + foo193: Long, + foo194: Long, + foo195: Long, + foo196: Long, + foo197: Long, + foo198: Long, + foo199: Long, + foo200: Long +) = foo1 + foo2 + +@main +def run = + println(foo(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200)) From a59a626f89748f306069c2b115f1f542c6c01d29 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 14:32:31 +0200 Subject: [PATCH 014/134] Simplify use of `toFunctionType` Make `isJava` by default false. [Cherry-picked eb8fba46a3ad685d3df98860a59bbfcf560556ba][modified] --- compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala | 4 ++-- compiler/src/dotty/tools/dotc/cc/Setup.scala | 6 +++--- compiler/src/dotty/tools/dotc/core/Types.scala | 3 ++- compiler/src/dotty/tools/dotc/transform/Recheck.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 039a9623ff5f..4d41ee55f3a9 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -630,7 +630,7 @@ class CheckCaptures extends Recheck, SymTransformer: private def toDepFun(args: List[Type], resultType: Type, isContextual: Boolean)(using Context): Type = MethodType.companion(isContextual = isContextual)(args, resultType) - .toFunctionType(isJava = false, alwaysDependent = true) + .toFunctionType(alwaysDependent = true) /** Turn `expected` into a dependent function when `actual` is dependent. */ private def alignDependentFunction(expected: Type, actual: Type)(using Context): Type = @@ -774,7 +774,7 @@ class CheckCaptures extends Recheck, SymTransformer: adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, (aargs1, ares1) => rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1) - .toFunctionType(isJava = false, alwaysDependent = true)) + .toFunctionType(alwaysDependent = true)) case actual: MethodType => adaptFun(actual, actual.paramInfos, actual.resType, expected, covariant, insertBox, (aargs1, ares1) => diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 94219da31bdf..463919e85893 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -40,7 +40,7 @@ extends tpd.TreeTraverser: MethodType.companion( isContextual = defn.isContextFunctionClass(tycon.classSymbol), )(argTypes, resType) - .toFunctionType(isJava = false, alwaysDependent = true) + .toFunctionType(alwaysDependent = true) /** If `tp` is an unboxed capturing type or a function returning an unboxed capturing type, * convert it to be boxed. @@ -57,7 +57,7 @@ extends tpd.TreeTraverser: case tp1 @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(tp1) => val boxedRinfo = recur(rinfo) if boxedRinfo eq rinfo then tp - else boxedRinfo.toFunctionType(isJava = false, alwaysDependent = true) + else boxedRinfo.toFunctionType(alwaysDependent = true) case tp1: MethodOrPoly => val res = tp1.resType val boxedRes = recur(res) @@ -233,7 +233,7 @@ extends tpd.TreeTraverser: tp.derivedAppliedType(tycon1, args.mapConserve(arg => this(arg))) case tp @ RefinedType(core, rname, rinfo: MethodType) if defn.isFunctionType(tp) => val rinfo1 = apply(rinfo) - if rinfo1 ne rinfo then rinfo1.toFunctionType(isJava = false, alwaysDependent = true) + if rinfo1 ne rinfo then rinfo1.toFunctionType(alwaysDependent = true) else tp case tp: MethodType => tp.derivedLambdaType( diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0bae5ce07722..f61beec02d82 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1858,11 +1858,12 @@ object Types { /** Turn type into a function type. * @pre this is a method type without parameter dependencies. + * @param isJava translate repeated params as as java `Array`s? * @param dropLast the number of trailing parameters that should be dropped * when forming the function type. * @param alwaysDependent if true, always create a dependent function type. */ - def toFunctionType(isJava: Boolean, dropLast: Int = 0, alwaysDependent: Boolean = false)(using Context): Type = this match { + def toFunctionType(isJava: Boolean = false, dropLast: Int = 0, alwaysDependent: Boolean = false)(using Context): Type = this match { case mt: MethodType if !mt.isParamDependent && !mt.hasErasedParams => val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast val isContextual = mt.isContextualMethod && !ctx.erasedTypes diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index f75c8f00dd16..503045779dce 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -324,7 +324,7 @@ abstract class Recheck extends Phase, SymTransformer: def recheckClosure(tree: Closure, pt: Type)(using Context): Type = if tree.tpt.isEmpty then - tree.meth.tpe.widen.toFunctionType(tree.meth.symbol.is(JavaDefined)) + tree.meth.tpe.widen.toFunctionType(isJava = tree.meth.symbol.is(JavaDefined)) else recheck(tree.tpt) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ff5dc5042eaf..f5d8e82de78e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -197,7 +197,7 @@ class Splicing extends MacroTransform: if tree.isTerm then if isCaptured(tree.symbol) then val tpe = tree.tpe.widenTermRefExpr match { - case tpw: MethodicType => tpw.toFunctionType(isJava = false) + case tpw: MethodicType => tpw.toFunctionType() case tpw => tpw } spliced(tpe)(capturedTerm(tree)) @@ -291,7 +291,7 @@ class Splicing extends MacroTransform: private def capturedTerm(tree: Tree)(using Context): Tree = val tpe = tree.tpe.widenTermRefExpr match - case tpw: MethodicType => tpw.toFunctionType(isJava = false) + case tpw: MethodicType => tpw.toFunctionType() case tpw => tpw capturedTerm(tree, tpe) From f8c6b90a66f4dfa12a52ea18f806626e4c953d28 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 14:37:08 +0200 Subject: [PATCH 015/134] Remove `dropLast` from `toFunctionType` If needed, use `derivedLambdaType` to drop the last parameters before converting to a function type. [Cherry-picked 3419d8a3ba912fa26dac5df30f78d08ab928c4e6][modified] --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 ++----- .../src/dotty/tools/dotc/typer/TypeAssigner.scala | 12 +++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f61beec02d82..755214954e61 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1859,20 +1859,17 @@ object Types { /** Turn type into a function type. * @pre this is a method type without parameter dependencies. * @param isJava translate repeated params as as java `Array`s? - * @param dropLast the number of trailing parameters that should be dropped - * when forming the function type. * @param alwaysDependent if true, always create a dependent function type. */ - def toFunctionType(isJava: Boolean = false, dropLast: Int = 0, alwaysDependent: Boolean = false)(using Context): Type = this match { + def toFunctionType(isJava: Boolean = false, alwaysDependent: Boolean = false)(using Context): Type = this match { case mt: MethodType if !mt.isParamDependent && !mt.hasErasedParams => - val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast val isContextual = mt.isContextualMethod && !ctx.erasedTypes val result1 = mt.nonDependentResultApprox match { case res: MethodType => res.toFunctionType(isJava) case res => res } val funType = defn.FunctionOf( - formals1 mapConserve (_.translateFromRepeated(toArray = isJava)), + mt.paramInfos.mapConserve(_.translateFromRepeated(toArray = isJava)), result1, isContextual) if alwaysDependent || mt.isResultDependent then RefinedType(funType, nme.apply, mt) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 4a81e6ff017d..b57866d43a37 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -401,7 +401,17 @@ trait TypeAssigner { def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(using Context): Closure = tree.withType( - if (target.isEmpty) meth.tpe.widen.toFunctionType(isJava = meth.symbol.is(JavaDefined), tree.env.length) + if target.isEmpty then + def methTypeWithoutEnv(info: Type): Type = info match + case mt: MethodType => + val dropLast = tree.env.length + val paramNames = mt.paramNames.dropRight(dropLast) + val paramInfos = mt.paramInfos.dropRight(dropLast) + mt.derivedLambdaType(paramNames, paramInfos) + case pt: PolyType => + pt.derivedLambdaType(resType = methTypeWithoutEnv(pt.resType)) + val methodicType = if tree.env.isEmpty then meth.tpe.widen else methTypeWithoutEnv(meth.tpe.widen) + methodicType.toFunctionType(isJava = meth.symbol.is(JavaDefined)) else target.tpe) def assignType(tree: untpd.CaseDef, pat: Tree, body: Tree)(using Context): CaseDef = { From c988d33ddc3fd3599982229676c58ebb117e33d0 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 14:40:24 +0200 Subject: [PATCH 016/134] Add `defn.RefinedFunctionOf` extractor [Cherry-picked 757a4bee8d493600ee71601c5ea6ae7b1f3a59f3][modified] --- .../src/dotty/tools/dotc/cc/CheckCaptures.scala | 10 +++++----- compiler/src/dotty/tools/dotc/cc/Setup.scala | 4 ++-- .../src/dotty/tools/dotc/core/Definitions.scala | 14 ++++++++++++++ compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 4d41ee55f3a9..a2dd3e450a50 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -187,7 +187,7 @@ class CheckCaptures extends Recheck, SymTransformer: capt.println(i"solving $t") refs.solve() traverse(parent) - case t @ RefinedType(_, nme.apply, rinfo) if defn.isFunctionType(t) => + case t @ defn.RefinedFunctionOf(rinfo) => traverse(rinfo) case tp: TypeVar => case tp: TypeRef => @@ -769,7 +769,7 @@ class CheckCaptures extends Recheck, SymTransformer: case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) => adaptFun(actual, args.init, args.last, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1)) - case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) => + case actual @ defn.RefinedFunctionOf(rinfo: MethodType) => // TODO Find a way to combine handling of generic and dependent function types (here and elsewhere) adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, (aargs1, ares1) => @@ -779,11 +779,11 @@ class CheckCaptures extends Recheck, SymTransformer: adaptFun(actual, actual.paramInfos, actual.resType, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedLambdaType(paramInfos = aargs1, resType = ares1)) - case actual @ RefinedType(p, nme, rinfo: PolyType) if defn.isFunctionType(actual) => + case actual @ defn.RefinedFunctionOf(rinfo: PolyType) => adaptTypeFun(actual, rinfo.resType, expected, covariant, insertBox, ares1 => val rinfo1 = rinfo.derivedLambdaType(rinfo.paramNames, rinfo.paramInfos, ares1) - val actual1 = actual.derivedRefinedType(p, nme, rinfo1) + val actual1 = actual.derivedRefinedType(actual.parent, actual.refinedName, rinfo1) actual1 ) case _ => @@ -996,7 +996,7 @@ class CheckCaptures extends Recheck, SymTransformer: case CapturingType(parent, refs) => healCaptureSet(refs) traverse(parent) - case tp @ RefinedType(parent, rname, rinfo: MethodType) if defn.isFunctionType(tp) => + case defn.RefinedFunctionOf(rinfo: MethodType) => traverse(rinfo) case tp: TermLambda => val saved = allowed diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 463919e85893..758486532512 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -54,7 +54,7 @@ extends tpd.TreeTraverser: val boxedRes = recur(res) if boxedRes eq res then tp else tp1.derivedAppliedType(tycon, args.init :+ boxedRes) - case tp1 @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(tp1) => + case tp1 @ defn.RefinedFunctionOf(rinfo: MethodType) => val boxedRinfo = recur(rinfo) if boxedRinfo eq rinfo then tp else boxedRinfo.toFunctionType(alwaysDependent = true) @@ -231,7 +231,7 @@ extends tpd.TreeTraverser: tp.derivedAppliedType(tycon1, args1 :+ res1) else tp.derivedAppliedType(tycon1, args.mapConserve(arg => this(arg))) - case tp @ RefinedType(core, rname, rinfo: MethodType) if defn.isFunctionType(tp) => + case defn.RefinedFunctionOf(rinfo: MethodType) => val rinfo1 = apply(rinfo) if rinfo1 ne rinfo then rinfo1.toFunctionType(alwaysDependent = true) else tp diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index cebc2cb67c45..2411899b1740 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1132,6 +1132,20 @@ class Definitions { case _ => None } + object RefinedFunctionOf { + /** Matches a refined `PolyFunction`/`FunctionN[...]`/`ContextFunctionN[...]`. + * Extracts the method type type and apply info. + */ + def unapply(tpe: RefinedType)(using Context): Option[MethodOrPoly] = { + tpe.refinedInfo match + case mt: MethodOrPoly + if tpe.refinedName == nme.apply + && (tpe.parent.derivesFrom(defn.PolyFunctionClass) || isFunctionNType(tpe.parent)) => + Some(mt) + case _ => None + } + } + object PolyFunctionOf { /** Matches a refined `PolyFunction` type and extracts the apply info. * diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 755214954e61..e09778786290 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4055,8 +4055,8 @@ object Types { tp.derivedAppliedType(tycon, addInto(args.head) :: Nil) case tp @ AppliedType(tycon, args) if defn.isFunctionNType(tp) => wrapConvertible(tp.derivedAppliedType(tycon, args.init :+ addInto(args.last))) - case tp @ RefinedType(parent, rname, rinfo) if defn.isFunctionType(tp) => - wrapConvertible(tp.derivedRefinedType(parent, rname, addInto(rinfo))) + case tp @ defn.RefinedFunctionOf(rinfo) => + wrapConvertible(tp.derivedRefinedType(tp.parent, tp.refinedName, addInto(rinfo))) case tp: MethodOrPoly => tp.derivedLambdaType(resType = addInto(tp.resType)) case ExprType(resType) => From 3dbb053fb3fcf1dca51349a5b04eb5beafcc23ce Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 14:42:31 +0200 Subject: [PATCH 017/134] Improve `defn.PolyFunctionOf` extractor * Only match `RefinedType` representing the `PolyFunction`. This will allow us to use `derivedRefinedType` on the function type. * Only match the refinement if it is a `MethodOrPoly`. `ExprType` is not a valid `PolyFunction` refinement. * Remove `dealias` in `PolyFunctionOf` extractor. There was only one case where this was necessary and it added unnecessary overhead. [Cherry-picked 60b2d0276f4a7b36b4b00152724aa8ab5e5f0e5d][modified] --- compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala | 2 +- compiler/src/dotty/tools/dotc/core/Definitions.scala | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index a2dd3e450a50..e6287bb3db5d 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -732,7 +732,7 @@ class CheckCaptures extends Recheck, SymTransformer: try val eres = expected.dealias.stripCapturing match - case RefinedType(_, _, rinfo: PolyType) => rinfo.resType + case defn.PolyFunctionOf(rinfo: PolyType) => rinfo.resType case expected: PolyType => expected.resType case _ => WildcardType diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2411899b1740..1c3a2c02bd6d 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1151,11 +1151,12 @@ class Definitions { * * Pattern: `PolyFunction { def apply: $pt }` */ - def unapply(ft: Type)(using Context): Option[PolyType] = ft.dealias match - case RefinedType(parent, nme.apply, pt: PolyType) - if parent.derivesFrom(defn.PolyFunctionClass) => - Some(pt) - case _ => None + def unapply(tpe: RefinedType)(using Context): Option[MethodOrPoly] = + tpe.refinedInfo match + case mt: MethodOrPoly + if tpe.refinedName == nme.apply && tpe.parent.derivesFrom(defn.PolyFunctionClass) => + Some(mt) + case _ => None } object ErasedFunctionOf { From c6ade8ba37ace882907cd30c5bb930b058d3b0d4 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 1 Sep 2023 12:53:39 +0200 Subject: [PATCH 018/134] Add regression test for issue 18493 [Cherry-picked b93763a4b4c2ea0394b6b18036b16d91a4971663] --- tests/neg/18493.check | 8 ++++++++ tests/neg/18493.scala | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/neg/18493.check create mode 100644 tests/neg/18493.scala diff --git a/tests/neg/18493.check b/tests/neg/18493.check new file mode 100644 index 000000000000..79a2872e71e8 --- /dev/null +++ b/tests/neg/18493.check @@ -0,0 +1,8 @@ +-- [E030] Match case Unreachable Error: tests/neg/18493.scala:6:9 ------------------------------------------------------ +6 | case "abc" => // error + | ^^^^^ + | Unreachable case +-- [E030] Match case Unreachable Error: tests/neg/18493.scala:12:9 ----------------------------------------------------- +12 | case "abc" => // error + | ^^^^^ + | Unreachable case diff --git a/tests/neg/18493.scala b/tests/neg/18493.scala new file mode 100644 index 000000000000..8dfb3bf923cc --- /dev/null +++ b/tests/neg/18493.scala @@ -0,0 +1,14 @@ +//> using options -Werror +object PartialFunctionNoWarning { + // nice warning + "abc" match { + case "abc" => + case "abc" => // error + } + + // no warnings + val pf: PartialFunction[String, Unit] = { + case "abc" => + case "abc" => // error + } +} \ No newline at end of file From 76ae98962d4f24e4de14bbce436d2f33121f6a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 11 Aug 2023 15:57:51 +0200 Subject: [PATCH 019/134] Fix #17115: Try to normalize while computing `typeSize`. [Cherry-picked ad29ce89624470de6204b242f0c3ee75fde727a3] --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 +++- tests/pos/i17115.scala | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17115.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index e09778786290..ad60fa460c9f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6397,7 +6397,9 @@ object Types { seen += tp tp match { case tp: AppliedType => - foldOver(n + 1, tp) + val tpNorm = tp.tryNormalize + if tpNorm.exists then apply(n, tpNorm) + else foldOver(n + 1, tp) case tp: RefinedType => foldOver(n + 1, tp) case tp: TypeRef if tp.info.isTypeAlias => diff --git a/tests/pos/i17115.scala b/tests/pos/i17115.scala new file mode 100644 index 000000000000..5a7cac5d0dc1 --- /dev/null +++ b/tests/pos/i17115.scala @@ -0,0 +1,9 @@ +trait A[T <: Tuple] { val x: Int } +given empty: A[EmptyTuple] with { val x = 1 } +given inductive[Tup <: NonEmptyTuple](using A[Tuple.Tail[Tup]]): A[Tup] with { val x = summon[A[Tuple.Tail[Tup]]].x + 1 } + +object Test: + def main(args: Array[String]): Unit = + println(summon[A[(String, String, String)]].x) //this line is fine + println(summon[A[(String, String, String, String)]].x) //this line gives error +end Test From 6f194ed1e5d7f5f399378824b8f10e16dcb7c335 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 14:43:25 +0200 Subject: [PATCH 020/134] Fix #18211: Add regression test. This is also fixed by the parent commit. [Cherry-picked eb18e53aa67acf69d310b1183f2ac0a9ca94d352][modified] --- .../test/dotc/pos-test-pickling.blacklist | 1 + tests/pos/i18211.scala | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/pos/i18211.scala diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 8aee79aa3806..85f1992d6ee4 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -54,6 +54,7 @@ i6505.scala i15158.scala i15155.scala i15827.scala +i18211.scala # Opaque type i5720.scala diff --git a/tests/pos/i18211.scala b/tests/pos/i18211.scala new file mode 100644 index 000000000000..c5ec30ba5d61 --- /dev/null +++ b/tests/pos/i18211.scala @@ -0,0 +1,39 @@ +import scala.compiletime.ops.int.* + +type AnyInt[A <: Int] <: Int = A match { + case _ => A +} + +type IndexOf[A, T <: Tuple] <: Int = T match { + case EmptyTuple => -1 + case A *: t => 0 + case _ *: t => + IndexOf[A, t] match { + case -1 => -1 + case AnyInt[a] => S[a] + } +} + +type Indexes[A, T <: Tuple] +object Indexes { + given of[A, T <: Tuple](using IndexOf[A, T] >= 0 =:= true)(using + index: ValueOf[IndexOf[A, T]], + next: Indexes[A, Tuple.Drop[T, S[IndexOf[A, T]]]] + ): Indexes[A, T] = ??? + + given empty[A, T <: Tuple](using IndexOf[A, T] =:= -1): Indexes[A, T] = ??? +} + +class GetAll[A]: + def apply[T <: Tuple](t: T)(using indexes: Indexes[A, T]): List[A] = ??? + +def getAll[A]: GetAll[A] = new GetAll[A] + +def test = + // the code here is trying to get all values from a tuple that has type [X] as a list + + // this works if there are only two strings in the tuple + getAll[String](("str1", 1, "str2", false)) + + //but this not compiles if there are more than two strings in the tuple + getAll[String](("str1", 1, "str2", false, "str3")) From 0fda02ce5aca3c896557544d94c1aa77345e120f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 25 Aug 2023 10:52:12 +0200 Subject: [PATCH 021/134] Propagate constant in result of inline match Fixes #13161 [Cherry-picked 2b9a62bfdde75d21b352351af05f5ea92bf5906c] --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 8 ++++++-- tests/pos/i13161.scala | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i13161.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 0d481ee8e0be..29d6a5365978 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -877,8 +877,12 @@ class Inliner(val call: tpd.Tree)(using Context): } case _ => rhs0 } - val (usedBindings, rhs2) = dropUnusedDefs(caseBindings, rhs1) - val rhs = seq(usedBindings, rhs2) + val rhs2 = rhs1 match { + case Typed(expr, tpt) if rhs1.span.isSynthetic => constToLiteral(expr) + case _ => constToLiteral(rhs1) + } + val (usedBindings, rhs3) = dropUnusedDefs(caseBindings, rhs2) + val rhs = seq(usedBindings, rhs3) inlining.println(i"""--- reduce: |$tree |--- to: diff --git a/tests/pos/i13161.scala b/tests/pos/i13161.scala new file mode 100644 index 000000000000..4c6b2c4c32d0 --- /dev/null +++ b/tests/pos/i13161.scala @@ -0,0 +1,8 @@ +transparent inline def f: String = + inline 10 match + case _ => + inline Some["foo"]("foo") match + case Some(x) => x + +def test = + inline val failMsg = f From 5f19c1d71656c8408a0a84c6037410aa7d5fd66f Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:04:16 +0200 Subject: [PATCH 022/134] Remove erasure logic from ContextFunctionType [Cherry-picked 350dfa76ec7b4a3f9747b7ef35243b6b1f2ebe95][modified] --- .../dotty/tools/dotc/core/Definitions.scala | 19 ++++++++----------- .../dotty/tools/dotc/transform/Bridges.scala | 17 ++++++++--------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 1c3a2c02bd6d..6e6321d10d9c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1882,17 +1882,14 @@ class Definitions { */ object ContextFunctionType: def unapply(tp: Type)(using Context): Option[(List[Type], Type, List[Boolean])] = - if ctx.erasedTypes then - atPhase(erasurePhase)(unapply(tp)) - else - asContextFunctionType(tp) match - case ErasedFunctionOf(mt) => - Some((mt.paramInfos, mt.resType, mt.erasedParams)) - case tp1 if tp1.exists => - val args = tp1.functionArgInfos - val erasedParams = erasedFunctionParameters(tp1) - Some((args.init, args.last, erasedParams)) - case _ => None + asContextFunctionType(tp) match + case PolyFunctionOf(mt: MethodType) => + Some((mt.paramInfos, mt.resType, mt.erasedParams)) + case tp1 if tp1.exists => + val args = tp1.functionArgInfos + val erasedParams = List.fill(functionArity(tp1)) { false } + Some((args.init, args.last, erasedParams)) + case _ => None /* Returns a list of erased booleans marking whether parameters are erased, for a function type. */ def erasedFunctionParameters(tp: Type)(using Context): List[Boolean] = tp.dealias match { diff --git a/compiler/src/dotty/tools/dotc/transform/Bridges.scala b/compiler/src/dotty/tools/dotc/transform/Bridges.scala index 569b16681cde..0156b6c26c40 100644 --- a/compiler/src/dotty/tools/dotc/transform/Bridges.scala +++ b/compiler/src/dotty/tools/dotc/transform/Bridges.scala @@ -129,25 +129,24 @@ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) { assert(ctx.typer.isInstanceOf[Erasure.Typer]) ctx.typer.typed(untpd.cpy.Apply(ref)(ref, args), member.info.finalResultType) else - val defn.ContextFunctionType(argTypes, resType, erasedParams) = tp: @unchecked - val anonFun = newAnonFun(ctx.owner, - MethodType( - argTypes.zip(erasedParams.padTo(argTypes.length, false)) - .flatMap((t, e) => if e then None else Some(t)), - resType), - coord = ctx.owner.coord) + val mtWithoutErasedParams = atPhase(erasurePhase) { + val defn.ContextFunctionType(argTypes, resType, erasedParams) = tp.dealias: @unchecked + val paramInfos = argTypes.zip(erasedParams).collect { case (argType, erased) if !erased => argType } + MethodType(paramInfos, resType) + } + val anonFun = newAnonFun(ctx.owner, mtWithoutErasedParams, coord = ctx.owner.coord) anonFun.info = transformInfo(anonFun, anonFun.info) def lambdaBody(refss: List[List[Tree]]) = val refs :: Nil = refss: @unchecked val expandedRefs = refs.map(_.withSpan(ctx.owner.span.endPos)) match case (bunchedParam @ Ident(nme.ALLARGS)) :: Nil => - argTypes.indices.toList.map(n => + mtWithoutErasedParams.paramInfos.indices.toList.map(n => bunchedParam .select(nme.primitive.arrayApply) .appliedTo(Literal(Constant(n)))) case refs1 => refs1 - expand(args ::: expandedRefs, resType, n - 1)(using ctx.withOwner(anonFun)) + expand(args ::: expandedRefs, mtWithoutErasedParams.resType, n - 1)(using ctx.withOwner(anonFun)) val unadapted = Closure(anonFun, lambdaBody) cpy.Block(unadapted)(unadapted.stats, From e0e421e268804c6e137b0970e580aa5c26f54266 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:06:40 +0200 Subject: [PATCH 023/134] Filter/count erased parameters directly on parameters types We can filter the erased parameters by looking at the `ErasedParamAnnot`. [Cherry-picked 7c0a848d0ed6330873a39129e6d02da169150fa7][modified] --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- .../src/dotty/tools/dotc/core/Definitions.scala | 7 +++---- .../src/dotty/tools/dotc/transform/Bridges.scala | 4 ++-- .../dotc/transform/ContextFunctionResults.scala | 16 +++++++++------- .../dotty/tools/dotc/typer/ErrorReporting.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index dfa04de22c17..79e47ca7b8df 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1146,7 +1146,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def etaExpandCFT(using Context): Tree = def expand(target: Tree, tp: Type)(using Context): Tree = tp match - case defn.ContextFunctionType(argTypes, resType, _) => + case defn.ContextFunctionType(argTypes, resType) => val anonFun = newAnonFun( ctx.owner, MethodType.companion(isContextual = true)(argTypes, resType), diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6e6321d10d9c..c5a798e2dcd7 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1881,14 +1881,13 @@ class Definitions { * types `As`, the result type `B` and a whether the type is an erased context function. */ object ContextFunctionType: - def unapply(tp: Type)(using Context): Option[(List[Type], Type, List[Boolean])] = + def unapply(tp: Type)(using Context): Option[(List[Type], Type)] = asContextFunctionType(tp) match case PolyFunctionOf(mt: MethodType) => - Some((mt.paramInfos, mt.resType, mt.erasedParams)) + Some((mt.paramInfos, mt.resType)) case tp1 if tp1.exists => val args = tp1.functionArgInfos - val erasedParams = List.fill(functionArity(tp1)) { false } - Some((args.init, args.last, erasedParams)) + Some((args.init, args.last)) case _ => None /* Returns a list of erased booleans marking whether parameters are erased, for a function type. */ diff --git a/compiler/src/dotty/tools/dotc/transform/Bridges.scala b/compiler/src/dotty/tools/dotc/transform/Bridges.scala index 0156b6c26c40..94f7b405c027 100644 --- a/compiler/src/dotty/tools/dotc/transform/Bridges.scala +++ b/compiler/src/dotty/tools/dotc/transform/Bridges.scala @@ -130,8 +130,8 @@ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) { ctx.typer.typed(untpd.cpy.Apply(ref)(ref, args), member.info.finalResultType) else val mtWithoutErasedParams = atPhase(erasurePhase) { - val defn.ContextFunctionType(argTypes, resType, erasedParams) = tp.dealias: @unchecked - val paramInfos = argTypes.zip(erasedParams).collect { case (argType, erased) if !erased => argType } + val defn.ContextFunctionType(argTypes, resType) = tp.dealias: @unchecked + val paramInfos = argTypes.filterNot(_.hasAnnotation(defn.ErasedParamAnnot)) MethodType(paramInfos, resType) } val anonFun = newAnonFun(ctx.owner, mtWithoutErasedParams, coord = ctx.owner.coord) diff --git a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala index b4eb71c541d3..41453aa62b50 100644 --- a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala +++ b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala @@ -20,7 +20,7 @@ object ContextFunctionResults: */ def annotateContextResults(mdef: DefDef)(using Context): Unit = def contextResultCount(rhs: Tree, tp: Type): Int = tp match - case defn.ContextFunctionType(_, resTpe, _) => + case defn.ContextFunctionType(_, resTpe) => rhs match case closureDef(meth) => 1 + contextResultCount(meth.rhs, resTpe) case _ => 0 @@ -58,7 +58,8 @@ object ContextFunctionResults: */ def contextResultsAreErased(sym: Symbol)(using Context): Boolean = def allErased(tp: Type): Boolean = tp.dealias match - case defn.ContextFunctionType(_, resTpe, erasedParams) => !erasedParams.contains(false) && allErased(resTpe) + case defn.ContextFunctionType(argTpes, resTpe) => + argTpes.forall(_.hasAnnotation(defn.ErasedParamAnnot)) && allErased(resTpe) case _ => true contextResultCount(sym) > 0 && allErased(sym.info.finalResultType) @@ -72,7 +73,7 @@ object ContextFunctionResults: integrateContextResults(rt, crCount) case tp: MethodOrPoly => tp.derivedLambdaType(resType = integrateContextResults(tp.resType, crCount)) - case defn.ContextFunctionType(argTypes, resType, erasedParams) => + case defn.ContextFunctionType(argTypes, resType) => MethodType(argTypes, integrateContextResults(resType, crCount - 1)) /** The total number of parameters of method `sym`, not counting @@ -83,9 +84,10 @@ object ContextFunctionResults: def contextParamCount(tp: Type, crCount: Int): Int = if crCount == 0 then 0 else - val defn.ContextFunctionType(params, resTpe, erasedParams) = tp: @unchecked + val defn.ContextFunctionType(params, resTpe) = tp: @unchecked val rest = contextParamCount(resTpe, crCount - 1) - if erasedParams.contains(true) then erasedParams.count(_ == false) + rest else params.length + rest + val nonErasedParams = params.count(!_.hasAnnotation(defn.ErasedParamAnnot)) + nonErasedParams + rest def normalParamCount(tp: Type): Int = tp.widenExpr.stripPoly match case mt @ MethodType(pnames) => @@ -103,7 +105,7 @@ object ContextFunctionResults: def recur(tp: Type, n: Int): Type = if n == 0 then tp else tp match - case defn.ContextFunctionType(_, resTpe, _) => recur(resTpe, n - 1) + case defn.ContextFunctionType(_, resTpe) => recur(resTpe, n - 1) recur(meth.info.finalResultType, depth) /** Should selection `tree` be eliminated since it refers to an `apply` @@ -118,7 +120,7 @@ object ContextFunctionResults: case Select(qual, name) => if name == nme.apply then qual.tpe match - case defn.ContextFunctionType(_, _, _) => + case defn.ContextFunctionType(_, _) => integrateSelect(qual, n + 1) case _ if defn.isContextFunctionClass(tree.symbol.maybeOwner) => // for TermRefs integrateSelect(qual, n + 1) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 25cbfdfec600..339d1f2f7bc6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -167,7 +167,7 @@ object ErrorReporting { val normPt = normalize(pt, pt) def contextFunctionCount(tp: Type): Int = tp.stripped match - case defn.ContextFunctionType(_, restp, _) => 1 + contextFunctionCount(restp) + case defn.ContextFunctionType(_, restp) => 1 + contextFunctionCount(restp) case _ => 0 def strippedTpCount = contextFunctionCount(tree.tpe) - contextFunctionCount(normTp) def strippedPtCount = contextFunctionCount(pt) - contextFunctionCount(normPt) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index df708057dd71..9aad20113154 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1885,7 +1885,7 @@ class Namer { typer: Typer => val originalTp = defaultParamType val approxTp = wildApprox(originalTp) approxTp.stripPoly match - case atp @ defn.ContextFunctionType(_, resType, _) + case atp @ defn.ContextFunctionType(_, resType) if !defn.isNonRefinedFunction(atp) // in this case `resType` is lying, gives us only the non-dependent upper bound || resType.existsPart(_.isInstanceOf[WildcardType], StopAt.Static, forceLazy = false) => originalTp From 71208c3710b2ba2b30b70d8ec07ae3c8ec04458c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 28 Aug 2023 18:36:10 +0200 Subject: [PATCH 024/134] Fix false positive in WUnused for renamed path-dependent imports [Cherry-picked 3b3dea383601fce2f21e94515710480b501b4194] --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 6 ++++-- tests/pos-special/fatal-warnings/i18366.scala | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/pos-special/fatal-warnings/i18366.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index eb3c37622e0d..0ba360fa21a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -669,8 +669,10 @@ object CheckUnused: val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) + val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType val selectionsToDealias = typeSelections ::: termSelections - val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) + def renamedSelection = if(sameTermPath) then sels.find(sel => sel.imported.name == sym.name) else None + val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel @@ -680,7 +682,7 @@ object CheckUnused: else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven && sel.bound.isEmpty) || sym.is(Implicit))) if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then - selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard) // selector with name or wildcard (or given) + selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard).orElse(renamedSelection) // selector with name or wildcard (or given) else None diff --git a/tests/pos-special/fatal-warnings/i18366.scala b/tests/pos-special/fatal-warnings/i18366.scala new file mode 100644 index 000000000000..bd25d607e897 --- /dev/null +++ b/tests/pos-special/fatal-warnings/i18366.scala @@ -0,0 +1,10 @@ +// scalac: -Wunused:all + +trait Builder { + def foo(): Unit +} + +def repro = + val builder: Builder = ??? + import builder.{foo => bar} + bar() \ No newline at end of file From 37ea99994e613b425eee3bd0a5a5135e05a1a8e6 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 29 Aug 2023 12:29:12 +0200 Subject: [PATCH 025/134] Check if owner of symbol exists [Cherry-picked 21641151411d9ea0a0b829bf11b82644cfb46c16] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 0ba360fa21a4..56e7bbcf3b89 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -669,7 +669,7 @@ object CheckUnused: val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) - val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType + val sameTermPath = qual.isTerm && sym.owner.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType val selectionsToDealias = typeSelections ::: termSelections def renamedSelection = if(sameTermPath) then sels.find(sel => sel.imported.name == sym.name) else None val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined From b697cfa106682c4cc50b06ef63df942059eebc06 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 29 Aug 2023 14:11:06 +0200 Subject: [PATCH 026/134] Revert "Check if owner of symbol exists" This reverts commit 21641151411d9ea0a0b829bf11b82644cfb46c16. [Cherry-picked c1f4e5a190e2d674c807c4352af4d8f2cb1825a9] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 56e7bbcf3b89..0ba360fa21a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -669,7 +669,7 @@ object CheckUnused: val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) - val sameTermPath = qual.isTerm && sym.owner.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType + val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType val selectionsToDealias = typeSelections ::: termSelections def renamedSelection = if(sameTermPath) then sels.find(sel => sel.imported.name == sym.name) else None val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined From f2fc101ed743f5d6cc41597eab2e3ef33d7909ee Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 29 Aug 2023 15:02:39 +0200 Subject: [PATCH 027/134] Replace def with lazy val [Cherry-picked 8bc81ef0a995b74125a1abfd9eb87584e32ddf21] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 0ba360fa21a4..4100ada0869f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -671,7 +671,7 @@ object CheckUnused: val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType val selectionsToDealias = typeSelections ::: termSelections - def renamedSelection = if(sameTermPath) then sels.find(sel => sel.imported.name == sym.name) else None + lazy val renamedSelection = if sameTermPath then sels.find(sel => sel.imported.name == sym.name) else None val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { From 71fd92df9bc1f78c4cd004d99b54253478221b5e Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 31 Aug 2023 11:56:04 +0200 Subject: [PATCH 028/134] Replace lazy val with val [Cherry-picked 6c6070a45fc7ec393825c07a71a194cf65c5955c] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 4100ada0869f..a3dbbfb202e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -671,7 +671,7 @@ object CheckUnused: val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) val sameTermPath = qual.isTerm && sym.exists && sym.owner.isType && qual.tpe.typeSymbol == sym.owner.asType val selectionsToDealias = typeSelections ::: termSelections - lazy val renamedSelection = if sameTermPath then sels.find(sel => sel.imported.name == sym.name) else None + val renamedSelection = if sameTermPath then sels.find(sel => sel.imported.name == sym.name) else None val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) || renamedSelection.isDefined def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { From 2da30980763e324960c7c60affbdef71b63f2130 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 14 Aug 2023 18:08:12 +0100 Subject: [PATCH 029/134] Reuse & switch to UncheckedTypePattern [Cherry-picked ee6d386fd4883aa9ea7a5f5c096c6c324a0ba1ce] --- .../dotty/tools/dotc/reporting/messages.scala | 4 +-- .../tools/dotc/transform/TypeTestsCasts.scala | 2 +- tests/neg/15981.check | 4 ++- tests/neg/i12253.check | 8 ++++-- tests/neg/i16728.check | 4 ++- tests/neg/i4812.check | 28 ++++++++++++++----- tests/neg/nowarn.check | 4 ++- 7 files changed, 39 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 03a3d8e57438..007f0a56f8d8 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -895,9 +895,9 @@ extends Message(PatternMatchExhaustivityID) { } } -class UncheckedTypePattern(msgFn: => String)(using Context) +class UncheckedTypePattern(argType: Type, whyNot: String)(using Context) extends PatternMatchMsg(UncheckedTypePatternID) { - def msg(using Context) = msgFn + def msg(using Context) = i"the type test for $argType cannot be checked at runtime because $whyNot" def explain(using Context) = i"""|Type arguments and type refinements are erased during compile time, thus it's |impossible to check them at run-time. diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index f5cb8eab73a4..9eaf92a83b7a 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -363,7 +363,7 @@ object TypeTestsCasts { if !isTrusted && !isUnchecked then val whyNot = whyUncheckable(expr.tpe, argType, tree.span) if whyNot.nonEmpty then - report.uncheckedWarning(em"the type test for $argType cannot be checked at runtime because $whyNot", expr.srcPos) + report.uncheckedWarning(UncheckedTypePattern(argType, whyNot), expr.srcPos) transformTypeTest(expr, argType, flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, dont't flag it even if it always false } diff --git a/tests/neg/15981.check b/tests/neg/15981.check index c4d677b486e9..10745839c566 100644 --- a/tests/neg/15981.check +++ b/tests/neg/15981.check @@ -1,4 +1,6 @@ --- Error: tests/neg/15981.scala:4:45 ----------------------------------------------------------------------------------- +-- [E092] Pattern Match Error: tests/neg/15981.scala:4:45 -------------------------------------------------------------- 4 | override def equals(any: Any): Boolean = any.isInstanceOf[PosInt] // error | ^^^ | the type test for PosInt cannot be checked at runtime because it's a local class + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12253.check b/tests/neg/i12253.check index 74fa3db47b0f..75a698249dee 100644 --- a/tests/neg/i12253.check +++ b/tests/neg/i12253.check @@ -1,9 +1,13 @@ --- Error: tests/neg/i12253.scala:13:10 --------------------------------------------------------------------------------- +-- [E092] Pattern Match Error: tests/neg/i12253.scala:13:10 ------------------------------------------------------------ 13 | case extractors.InlinedLambda(_, Select(_, name)) => Expr(name) // error // error | ^ |the type test for extractors.q2.reflect.Term cannot be checked at runtime because it refers to an abstract type member or type parameter --- Error: tests/neg/i12253.scala:13:38 --------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i12253.scala:13:38 ------------------------------------------------------------ 13 | case extractors.InlinedLambda(_, Select(_, name)) => Expr(name) // error // error | ^ |the type test for q1.reflect.Select cannot be checked at runtime because it refers to an abstract type member or type parameter + | + | longer explanation available when compiling with `-explain` there was 1 deprecation warning; re-run with -deprecation for details diff --git a/tests/neg/i16728.check b/tests/neg/i16728.check index 9bc8b7457ce2..93cc215696c2 100644 --- a/tests/neg/i16728.check +++ b/tests/neg/i16728.check @@ -1,4 +1,6 @@ --- Error: tests/neg/i16728.scala:18:11 --------------------------------------------------------------------------------- +-- [E092] Pattern Match Error: tests/neg/i16728.scala:18:11 ------------------------------------------------------------ 18 | case tx : C[Int]#X => // error | ^ | the type test for C[Int] cannot be checked at runtime because its type arguments can't be determined from A + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i4812.check b/tests/neg/i4812.check index 275cda56defe..f4aee0e35dde 100644 --- a/tests/neg/i4812.check +++ b/tests/neg/i4812.check @@ -1,28 +1,42 @@ --- Error: tests/neg/i4812.scala:8:11 ----------------------------------------------------------------------------------- +-- [E092] Pattern Match Error: tests/neg/i4812.scala:8:11 -------------------------------------------------------------- 8 | case prev: A => // error: the type test for A cannot be checked at runtime | ^ | the type test for A cannot be checked at runtime because it's a local class --- Error: tests/neg/i4812.scala:18:11 ---------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i4812.scala:18:11 ------------------------------------------------------------- 18 | case prev: A => // error: the type test for A cannot be checked at runtime | ^ | the type test for A cannot be checked at runtime because it's a local class --- Error: tests/neg/i4812.scala:28:11 ---------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i4812.scala:28:11 ------------------------------------------------------------- 28 | case prev: A => // error: the type test for A cannot be checked at runtime | ^ | the type test for A cannot be checked at runtime because it's a local class --- Error: tests/neg/i4812.scala:38:11 ---------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i4812.scala:38:11 ------------------------------------------------------------- 38 | case prev: A => // error: the type test for A cannot be checked at runtime | ^ | the type test for A cannot be checked at runtime because it's a local class --- Error: tests/neg/i4812.scala:50:13 ---------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i4812.scala:50:13 ------------------------------------------------------------- 50 | case prev: A => // error: the type test for A cannot be checked at runtime | ^ | the type test for A cannot be checked at runtime because it's a local class --- Error: tests/neg/i4812.scala:60:11 ---------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i4812.scala:60:11 ------------------------------------------------------------- 60 | case prev: A => // error: the type test for A cannot be checked at runtime | ^ | the type test for A cannot be checked at runtime because it's a local class --- Error: tests/neg/i4812.scala:96:11 ---------------------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Error: tests/neg/i4812.scala:96:11 ------------------------------------------------------------- 96 | case x: B => // error: the type test for B cannot be checked at runtime | ^ | the type test for B cannot be checked at runtime because it's a local class + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/nowarn.check b/tests/neg/nowarn.check index 3e16314f643e..a8075335290a 100644 --- a/tests/neg/nowarn.check +++ b/tests/neg/nowarn.check @@ -66,10 +66,12 @@ Matching filters for @nowarn or -Wconf: 49 |def t7c = f // warning (deprecation) | ^ | method f is deprecated --- Unchecked Warning: tests/neg/nowarn.scala:55:7 ---------------------------------------------------------------------- +-- [E092] Pattern Match Unchecked Warning: tests/neg/nowarn.scala:55:7 ------------------------------------------------- 55 | case _: List[Int] => 0 // warning (patmat, unchecked) | ^ |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from Any + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg/nowarn.scala:33:1 ---------------------------------------------------------------------------------- 33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) |^^^^^^^^^^^^^^^ From 92ba54a239aae74a25fbd99c02c02133f428591a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 14 Aug 2023 18:10:54 +0100 Subject: [PATCH 030/134] Vulpix: Extract common code & drop unused suppressErrors [Cherry-picked 845bb1c4c63b110513080c6b4872aca49b348795] --- .../dotty/tools/vulpix/ParallelTesting.scala | 91 +++++++++---------- 1 file changed, 41 insertions(+), 50 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index bccbcbee29e1..6880b2592b1b 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -226,14 +226,14 @@ trait ParallelTesting extends RunnerOrchestration { self => Try(testSource match { case testSource @ JointCompilationSource(name, files, flags, outDir, fromTasty, decompilation) => val reporter = - if (fromTasty) compileFromTasty(flags, suppressErrors, outDir) - else compile(testSource.sourceFiles, flags, suppressErrors, outDir) + if (fromTasty) compileFromTasty(flags, outDir) + else compile(testSource.sourceFiles, flags, outDir) List(reporter) case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => testSource.compilationGroups.map { (group, files) => if group.compiler.isEmpty then - compile(files, flags, suppressErrors, outDir) + compile(files, flags, outDir) else compileWithOtherCompiler(group.compiler, files, flags, outDir) } @@ -469,7 +469,7 @@ trait ParallelTesting extends RunnerOrchestration { self => registerCompletion() throw e - protected def compile(files0: Array[JFile], flags0: TestFlags, suppressErrors: Boolean, targetDir: JFile): TestReporter = { + protected def compile(files0: Array[JFile], flags0: TestFlags, targetDir: JFile): TestReporter = { import scala.util.Properties.* def flattenFiles(f: JFile): Array[JFile] = @@ -634,7 +634,7 @@ trait ParallelTesting extends RunnerOrchestration { self => reporter - protected def compileFromTasty(flags0: TestFlags, suppressErrors: Boolean, targetDir: JFile): TestReporter = { + protected def compileFromTasty(flags0: TestFlags, targetDir: JFile): TestReporter = { val tastyOutput = new JFile(targetDir.getPath + "_from-tasty") tastyOutput.mkdir() val flags = flags0 and ("-d", tastyOutput.getPath) and "-from-tasty" @@ -653,6 +653,12 @@ trait ParallelTesting extends RunnerOrchestration { self => private def mkLogLevel = if suppressErrors || suppressAllOutput then ERROR + 1 else ERROR private def mkReporter = TestReporter.reporter(realStdout, logLevel = mkLogLevel) + protected def diffCheckfile(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable) = + checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger)) + + private def reporterOutputLines(reporters: Seq[TestReporter]): List[String] = + reporters.flatMap(_.consoleOutput.split("\n")).toList + private[ParallelTesting] def executeTestSuite(): this.type = { assert(testSourcesCompleted == 0, "not allowed to re-use a `CompileRun`") if filteredSources.nonEmpty then @@ -808,10 +814,7 @@ trait ParallelTesting extends RunnerOrchestration { self => end maybeFailureMessage override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable): Unit = - checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger)) - - def reporterOutputLines(reporters: Seq[TestReporter]): List[String] = - reporters.flatMap(_.consoleOutput.split("\n")).toList + diffCheckfile(testSource, reporters, logger) // In neg-tests we allow two or three types of error annotations. // Normally, `// error` must be annotated on the correct line number. @@ -1014,20 +1017,8 @@ trait ParallelTesting extends RunnerOrchestration { self => * compilation without generating errors and that they do not crash the * compiler */ - def checkCompile()(implicit summaryReport: SummaryReporting): this.type = { - val test = new PosTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite() - - cleanup() - - if (!shouldFail && test.didFail) { - fail(s"Expected no errors when compiling, failed for the following reason(s):\n${reasonsForFailure(test)}\n") - } - else if (shouldFail && !test.didFail && test.skipCount == 0) { - fail("Pos test should have failed, but didn't") - } - - this - } + def checkCompile()(implicit summaryReport: SummaryReporting): this.type = + checkPass(new PosTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput), "Pos") /** Creates a "neg" test run, which makes sure that each test generates the * correct number of errors at the correct positions. It also makes sure @@ -1047,35 +1038,16 @@ trait ParallelTesting extends RunnerOrchestration { self => end checkExpectedErrors /** Creates a "fuzzy" test run, which makes sure that each test compiles (or not) without crashing */ - def checkNoCrash()(implicit summaryReport: SummaryReporting): this.type = { - val test = new NoCrashTest(targets, times, threadLimit, shouldSuppressOutput).executeTestSuite() - - cleanup() - - if (test.didFail) { - fail("Fuzzy test shouldn't have crashed, but did") - } - - this - } + def checkNoCrash()(implicit summaryReport: SummaryReporting): this.type = + checkFail(new NoCrashTest(targets, times, threadLimit, shouldSuppressOutput), "Fuzzy") /** Creates a "run" test run, which is a superset of "pos". In addition to * making sure that all tests pass compilation and that they do not crash * the compiler; it also makes sure that all tests can run with the * expected output */ - def checkRuns()(implicit summaryReport: SummaryReporting): this.type = { - val test = new RunTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite() - - cleanup() - - if !shouldFail && test.didFail then - fail(s"Run test failed, but should not, reasons:\n${ reasonsForFailure(test) }") - else if shouldFail && !test.didFail && test.skipCount == 0 then - fail("Run test should have failed, but did not") - - this - } + def checkRuns()(implicit summaryReport: SummaryReporting): this.type = + checkPass(new RunTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput), "Run") /** Tests `-rewrite`, which makes sure that the rewritten files still compile * and agree with the expected result (if specified). @@ -1100,15 +1072,34 @@ trait ParallelTesting extends RunnerOrchestration { self => target.copy(dir = copyToDir(outDir, dir)) } - val test = new RewriteTest(copiedTargets, checkFileMap, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite() + val test = new RewriteTest(copiedTargets, checkFileMap, times, threadLimit, shouldFail || shouldSuppressOutput) + + checkFail(test, "Rewrite") + } + + private def checkPass(test: Test, desc: String): this.type = + test.executeTestSuite() cleanup() - if test.didFail then - fail("Rewrite test failed") + if !shouldFail && test.didFail then + fail(s"$desc test failed, but should not, reasons:\n${reasonsForFailure(test)}") + else if shouldFail && !test.didFail && test.skipCount == 0 then + fail(s"$desc test should have failed, but didn't") + + this + + private def checkFail(test: Test, desc: String): this.type = + test.executeTestSuite() + + cleanup() + + if shouldFail && !test.didFail && test.skipCount == 0 then + fail(s"$desc test shouldn't have failed, but did. Reasons:\n${reasonsForFailure(test)}") + else if !shouldFail && test.didFail then + fail(s"$desc test failed") this - } /** Deletes output directories and files */ private def cleanup(): this.type = { From e576ac6592d748c36543a520aa2e19fe40c5334f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 14 Aug 2023 18:13:43 +0100 Subject: [PATCH 031/134] Vulpix: Introduce WarnTest/checkWarnings [Cherry-picked 4740017dc2e13ff87aa85dc0887d68469a232764] --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 9 +++++++++ compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 74c5c0dbb1e1..01c369d40a5d 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -108,6 +108,15 @@ class CompilationTests { ).times(2).checkCompile() } + // Warning tests ------------------------------------------------------------ + + @Test def warn: Unit = { + implicit val testGroup: TestGroup = TestGroup("compileWarn") + aggregateTests( + compileFilesInDir("tests/warn", defaultOptions), + ).checkWarnings() + } + // Negative tests ------------------------------------------------------------ @Test def negAll: Unit = { diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 6880b2592b1b..bcf17c37fa0b 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -723,6 +723,12 @@ trait ParallelTesting extends RunnerOrchestration { self => private final class PosTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting) extends Test(testSources, times, threadLimit, suppressAllOutput) + private final class WarnTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting) + extends Test(testSources, times, threadLimit, suppressAllOutput): + override def suppressErrors = true + override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable): Unit = + diffCheckfile(testSource, reporters, logger) + private final class RewriteTest(testSources: List[TestSource], checkFiles: Map[JFile, JFile], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting) extends Test(testSources, times, threadLimit, suppressAllOutput) { private def verifyOutput(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable) = { @@ -1020,6 +1026,9 @@ trait ParallelTesting extends RunnerOrchestration { self => def checkCompile()(implicit summaryReport: SummaryReporting): this.type = checkPass(new PosTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput), "Pos") + def checkWarnings()(implicit summaryReport: SummaryReporting): this.type = + checkPass(new WarnTest(targets, times, threadLimit, shouldFail || shouldSuppressOutput), "Warn") + /** Creates a "neg" test run, which makes sure that each test generates the * correct number of errors at the correct positions. It also makes sure * that none of these tests crashes the compiler. From 333fa6c323bc38db9d0bc6bbb355feb92c25d3e0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 9 Aug 2023 19:01:09 +0100 Subject: [PATCH 032/134] Unsuppress unchecked warnings And check it with a pos checkfile. [Cherry-picked 904583460a1649e248949b329ca7fdb3aa4afe01] --- .../dotty/tools/dotc/reporting/Message.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 1 + .../tools/dotc/transform/TypeTestsCasts.scala | 4 +- .../tools/dotc/transform/patmat/Space.scala | 16 +++--- tests/neg-deep-subtype/enum-approx2.scala | 12 ---- tests/pending/neg/i16451.check | 24 -------- tests/warn/enum-approx2.check | 20 +++++++ tests/warn/enum-approx2.scala | 10 ++++ tests/{neg-deep-subtype => warn}/i11178.scala | 2 - tests/warn/i16451.check | 56 +++++++++++++++++++ tests/{pending/neg => warn}/i16451.scala | 22 +++++--- tests/warn/i5826.check | 34 +++++++++++ tests/{neg-deep-subtype => warn}/i5826.scala | 6 +- tests/{neg-deep-subtype => warn}/i8932.scala | 2 - .../suppressed-type-test-warnings.scala | 2 - 15 files changed, 148 insertions(+), 65 deletions(-) delete mode 100644 tests/neg-deep-subtype/enum-approx2.scala delete mode 100644 tests/pending/neg/i16451.check create mode 100644 tests/warn/enum-approx2.check create mode 100644 tests/warn/enum-approx2.scala rename tests/{neg-deep-subtype => warn}/i11178.scala (94%) create mode 100644 tests/warn/i16451.check rename tests/{pending/neg => warn}/i16451.scala (60%) create mode 100644 tests/warn/i5826.check rename tests/{neg-deep-subtype => warn}/i5826.scala (88%) rename tests/{neg-deep-subtype => warn}/i8932.scala (88%) rename tests/{neg => warn}/suppressed-type-test-warnings.scala (95%) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 1bfcf5c2f9dd..c971719a1712 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -377,7 +377,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => override def canExplain = true /** Override with `true` for messages that should always be shown even if their - * position overlaps another messsage of a different class. On the other hand + * position overlaps another message of a different class. On the other hand * multiple messages of the same class with overlapping positions will lead * to only a single message of that class to be issued. */ diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 007f0a56f8d8..f43b19708258 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -897,6 +897,7 @@ extends Message(PatternMatchExhaustivityID) { class UncheckedTypePattern(argType: Type, whyNot: String)(using Context) extends PatternMatchMsg(UncheckedTypePatternID) { + override def showAlways = true def msg(using Context) = i"the type test for $argType cannot be checked at runtime because $whyNot" def explain(using Context) = i"""|Type arguments and type refinements are erased during compile time, thus it's diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 9eaf92a83b7a..f682b54ae731 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -74,7 +74,7 @@ object TypeTestsCasts { }.apply(tp) /** Returns true if the type arguments of `P` can be determined from `X` */ - def typeArgsTrivial(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) { + def typeArgsDeterminable(X: Type, P: AppliedType)(using Context) = inContext(ctx.fresh.setExploreTyperState().setFreshGADTBounds) { val AppliedType(tycon, _) = P def underlyingLambda(tp: Type): TypeLambda = tp.ensureLambdaSub match { @@ -155,7 +155,7 @@ object TypeTestsCasts { case x => // always false test warnings are emitted elsewhere TypeComparer.provablyDisjoint(x, tpe.derivedAppliedType(tycon, targs.map(_ => WildcardType))) - || typeArgsTrivial(X, tpe) + || typeArgsDeterminable(X, tpe) ||| i"its type arguments can't be determined from $X" } case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 2464ca448763..90039422548e 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -394,7 +394,7 @@ object SpaceEngine { project(pat) case Typed(_, tpt) => - Typ(erase(tpt.tpe.stripAnnots, isValue = true), decomposed = false) + Typ(erase(tpt.tpe.stripAnnots, isValue = true, isTyped = true), decomposed = false) case This(_) => Typ(pat.tpe.stripAnnots, decomposed = false) @@ -462,24 +462,26 @@ object SpaceEngine { * If `isValue` is true, then pattern-bound symbols are erased to its upper bound. * This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala. */ - private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false)(using Context): Type = - trace(i"erase($tp${if inArray then " inArray" else ""}${if isValue then " isValue" else ""})", debug)(tp match { + private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false, isTyped: Boolean = false)(using Context): Type = + trace(i"erase($tp${if inArray then " inArray" else ""}${if isValue then " isValue" else ""}${if isTyped then " isTyped" else ""})", debug)(tp match { case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isPatternBound => WildcardType case tp @ AppliedType(tycon, args) => val inArray = tycon.isRef(defn.ArrayClass) - val args2 = args.map(arg => erase(arg, inArray = inArray, isValue = false)) + val args2 = + if isTyped && !inArray then args.map(_ => WildcardType) + else args.map(arg => erase(arg, inArray = inArray, isValue = false)) tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) case tp @ OrType(tp1, tp2) => - OrType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue), tp.isSoft) + OrType(erase(tp1, inArray, isValue, isTyped), erase(tp2, inArray, isValue, isTyped), tp.isSoft) case AndType(tp1, tp2) => - AndType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue)) + AndType(erase(tp1, inArray, isValue, isTyped), erase(tp2, inArray, isValue, isTyped)) case tp @ RefinedType(parent, _, _) => - erase(parent, inArray, isValue) + erase(parent, inArray, isValue, isTyped) case tref: TypeRef if tref.symbol.isPatternBound => if inArray then tref.underlying diff --git a/tests/neg-deep-subtype/enum-approx2.scala b/tests/neg-deep-subtype/enum-approx2.scala deleted file mode 100644 index bf114d9c8569..000000000000 --- a/tests/neg-deep-subtype/enum-approx2.scala +++ /dev/null @@ -1,12 +0,0 @@ -//> using options -Xfatal-warnings - -sealed trait Exp[T] -case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] - -class Test { - def eval(e: Fun[Int, Int]) = e match { - case Fun(x: Fun[Int, Double]) => ??? // error - case Fun(x: Exp[Int => String]) => ??? // error - case _ => - } -} diff --git a/tests/pending/neg/i16451.check b/tests/pending/neg/i16451.check deleted file mode 100644 index e53085e8eafa..000000000000 --- a/tests/pending/neg/i16451.check +++ /dev/null @@ -1,24 +0,0 @@ --- Error: tests/neg/i16451.scala:13:9 ---------------------------------------------------------------------------------- -13 | case x: Wrapper[Color.Red.type] => Some(x) // error - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] --- Error: tests/neg/i16451.scala:21:9 ---------------------------------------------------------------------------------- -21 | case x: Wrapper[Color.Red.type] => Some(x) // error - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any --- Error: tests/neg/i16451.scala:25:9 ---------------------------------------------------------------------------------- -25 | case x: Wrapper[Color.Red.type] => Some(x) // error - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] --- Error: tests/neg/i16451.scala:29:9 ---------------------------------------------------------------------------------- -29 | case x: Wrapper[Color.Red.type] => Some(x) // error - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1 --- Error: tests/neg/i16451.scala:34:11 --------------------------------------------------------------------------------- -34 | case x: Wrapper[Color.Red.type] => x // error - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] --- Error: tests/neg/i16451.scala:39:11 --------------------------------------------------------------------------------- -39 | case x: Wrapper[Color.Red.type] => x // error - | ^ - |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] diff --git a/tests/warn/enum-approx2.check b/tests/warn/enum-approx2.check new file mode 100644 index 000000000000..01e1d8f5addd --- /dev/null +++ b/tests/warn/enum-approx2.check @@ -0,0 +1,20 @@ +-- [E030] Match case Unreachable Warning: tests/warn/enum-approx2.scala:7:12 ------------------------------------------- +7 | case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // warn: unchecked + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Unreachable case +-- [E121] Pattern Match Warning: tests/warn/enum-approx2.scala:8:9 ----------------------------------------------------- +8 | case _ => // warn: unreachable-only-null + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). +-- [E092] Pattern Match Unchecked Warning: tests/warn/enum-approx2.scala:6:13 ------------------------------------------ +6 | case Fun(x: Fun[Int, Double]) => ??? // warn: unchecked + | ^ + |the type test for Fun[Int, Double] cannot be checked at runtime because its type arguments can't be determined from Exp[Int => Int] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/enum-approx2.scala:7:13 ------------------------------------------ +7 | case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // warn: unchecked + | ^ + |the type test for Exp[Int => String] cannot be checked at runtime because its type arguments can't be determined from Exp[Int => Int] + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/enum-approx2.scala b/tests/warn/enum-approx2.scala new file mode 100644 index 000000000000..2c2563a8b2f1 --- /dev/null +++ b/tests/warn/enum-approx2.scala @@ -0,0 +1,10 @@ +sealed trait Exp[T] +case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] + +class Test { + def eval(e: Fun[Int, Int]) = e match { + case Fun(x: Fun[Int, Double]) => ??? // warn: unchecked + case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // warn: unchecked + case _ => // warn: unreachable-only-null + } +} diff --git a/tests/neg-deep-subtype/i11178.scala b/tests/warn/i11178.scala similarity index 94% rename from tests/neg-deep-subtype/i11178.scala rename to tests/warn/i11178.scala index 2ac4f9e07262..47e8b4c3acab 100644 --- a/tests/neg-deep-subtype/i11178.scala +++ b/tests/warn/i11178.scala @@ -1,5 +1,3 @@ -//> using options -Xfatal-warnings - trait Box[+T] case class Foo[+S](s: S) extends Box[S] diff --git a/tests/warn/i16451.check b/tests/warn/i16451.check new file mode 100644 index 000000000000..2bc469af480b --- /dev/null +++ b/tests/warn/i16451.check @@ -0,0 +1,56 @@ +-- [E030] Match case Unreachable Warning: tests/warn/i16451.scala:14:9 ------------------------------------------------- +14 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Unreachable case +-- [E030] Match case Unreachable Warning: tests/warn/i16451.scala:22:9 ------------------------------------------------- +22 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Unreachable case +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:13:9 ------------------------------------------------ +13 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:14:9 ------------------------------------------------ +14 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked + | ^ + |the type test for Wrapper[(Color.Green : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:21:9 ------------------------------------------------ +21 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:22:9 ------------------------------------------------ +22 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked + | ^ + |the type test for Wrapper[(Color.Green : Color)] cannot be checked at runtime because its type arguments can't be determined from Any + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:25:9 ------------------------------------------------ +25 | case x: Wrapper[Color.Red.type] => Some(x) // error: unreachable // error: unchecked + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:29:9 ------------------------------------------------ +29 | case x: Wrapper[Color.Red.type] => Some(x) + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1 + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:34:11 ----------------------------------------------- +34 | case x: Wrapper[Color.Red.type] => x + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:39:11 ----------------------------------------------- +39 | case x: Wrapper[Color.Red.type] => x + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] + | + | longer explanation available when compiling with `-explain` diff --git a/tests/pending/neg/i16451.scala b/tests/warn/i16451.scala similarity index 60% rename from tests/pending/neg/i16451.scala rename to tests/warn/i16451.scala index 3a93a97e1f03..b11def9ec2ba 100644 --- a/tests/pending/neg/i16451.scala +++ b/tests/warn/i16451.scala @@ -1,38 +1,42 @@ -//> using options -Werror +// enum Color: case Red, Green +//sealed trait Color +//object Color: +// case object Red extends Color +// case object Green extends Color case class Wrapper[A](value: A) object Test: def test_correct(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // error - case null => None + case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked + case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked def test_different(x: Wrapper[Color]): Option[Wrapper[Color]] = x match case x @ Wrapper(_: Color.Red.type) => Some(x) case x @ Wrapper(_: Color.Green.type) => None def test_any(x: Any): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // error - case _ => None + case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked + case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked def test_wrong(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // error + case x: Wrapper[Color.Red.type] => Some(x) // error: unreachable // error: unchecked case null => None def t2[A1 <: Wrapper[Color]](x: A1): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // error + case x: Wrapper[Color.Red.type] => Some(x) case null => None def test_wrong_seq(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = xs.collect { - case x: Wrapper[Color.Red.type] => x // error + case x: Wrapper[Color.Red.type] => x } def test_wrong_seq2(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = xs.collect { x => x match - case x: Wrapper[Color.Red.type] => x // error + case x: Wrapper[Color.Red.type] => x } def main(args: Array[String]): Unit = diff --git a/tests/warn/i5826.check b/tests/warn/i5826.check new file mode 100644 index 000000000000..d29df1c8f34b --- /dev/null +++ b/tests/warn/i5826.check @@ -0,0 +1,34 @@ +-- [E121] Pattern Match Warning: tests/warn/i5826.scala:9:9 ------------------------------------------------------------ +9 | case _ => 0 // warn: unreachable-only-null + | ^ + | Unreachable case except for null (if this is intentional, consider writing case null => instead). +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:3:9 -------------------------------------------------- +3 | case ls: List[Int] => ls.head // error, A = List[String] + | ^ + | the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from A + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:8:9 -------------------------------------------------- +8 | case ls: List[Int] => ls.head // warn: unchecked + | ^ + |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from List[String] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:17:9 ------------------------------------------------- +17 | case ls: A[X] => 4 // error + | ^ + |the type test for Foo.this.A[X] cannot be checked at runtime because its type arguments can't be determined from Foo.this.B[X] + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:22:9 ------------------------------------------------- +22 | case ls: List[Int] => ls.head // error, List extends Int => T + | ^ + |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from A => Int + | + | longer explanation available when compiling with `-explain` +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:28:54 ------------------------------------------------ +28 | def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // error + | ^ + |the type test for Foo.this.C[String] cannot be checked at runtime because its type arguments can't be determined from Foo.this.A[T] + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-deep-subtype/i5826.scala b/tests/warn/i5826.scala similarity index 88% rename from tests/neg-deep-subtype/i5826.scala rename to tests/warn/i5826.scala index 2f6bfb9f8127..e7eda8826139 100644 --- a/tests/neg-deep-subtype/i5826.scala +++ b/tests/warn/i5826.scala @@ -1,5 +1,3 @@ -//> using options -Xfatal-warnings - class Foo { def test[A]: (List[Int] | A) => Int = { case ls: List[Int] => ls.head // error, A = List[String] @@ -7,8 +5,8 @@ class Foo { } def test2: List[Int] | List[String] => Int = { - case ls: List[Int] => ls.head // error - case _ => 0 + case ls: List[Int] => ls.head // warn: unchecked + case _ => 0 // warn: unreachable-only-null } trait A[T] diff --git a/tests/neg-deep-subtype/i8932.scala b/tests/warn/i8932.scala similarity index 88% rename from tests/neg-deep-subtype/i8932.scala rename to tests/warn/i8932.scala index dc2ae3358410..84d2f7d4990a 100644 --- a/tests/neg-deep-subtype/i8932.scala +++ b/tests/warn/i8932.scala @@ -1,5 +1,3 @@ -//> using options -Xfatal-warnings - sealed trait Foo[+A] case class Bar[A]() extends Foo[A] diff --git a/tests/neg/suppressed-type-test-warnings.scala b/tests/warn/suppressed-type-test-warnings.scala similarity index 95% rename from tests/neg/suppressed-type-test-warnings.scala rename to tests/warn/suppressed-type-test-warnings.scala index 2414f13e73cb..92d86b3307e5 100644 --- a/tests/neg/suppressed-type-test-warnings.scala +++ b/tests/warn/suppressed-type-test-warnings.scala @@ -1,5 +1,3 @@ -//> using options -Xfatal-warnings - object Test { sealed trait Foo[A, B] final case class Bar[X](x: X) extends Foo[X, X] From 5d547208c4921a38de58138eb2492c947c385508 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 11 Aug 2023 16:26:10 +0100 Subject: [PATCH 033/134] Drop showAlways on UncheckedTypePattern [Cherry-picked cae678648d00411b0cb55b03448ebeb46ab71065] --- .../dotty/tools/dotc/reporting/messages.scala | 1 - tests/warn/enum-approx2.check | 8 +------- tests/warn/enum-approx2.scala | 2 +- tests/warn/i16451.check | 16 ++-------------- tests/warn/i16451.scala | 4 ++-- 5 files changed, 6 insertions(+), 25 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index f43b19708258..007f0a56f8d8 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -897,7 +897,6 @@ extends Message(PatternMatchExhaustivityID) { class UncheckedTypePattern(argType: Type, whyNot: String)(using Context) extends PatternMatchMsg(UncheckedTypePatternID) { - override def showAlways = true def msg(using Context) = i"the type test for $argType cannot be checked at runtime because $whyNot" def explain(using Context) = i"""|Type arguments and type refinements are erased during compile time, thus it's diff --git a/tests/warn/enum-approx2.check b/tests/warn/enum-approx2.check index 01e1d8f5addd..a75c15b424ff 100644 --- a/tests/warn/enum-approx2.check +++ b/tests/warn/enum-approx2.check @@ -1,5 +1,5 @@ -- [E030] Match case Unreachable Warning: tests/warn/enum-approx2.scala:7:12 ------------------------------------------- -7 | case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // warn: unchecked +7 | case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // also: unchecked (hidden) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | Unreachable case -- [E121] Pattern Match Warning: tests/warn/enum-approx2.scala:8:9 ----------------------------------------------------- @@ -12,9 +12,3 @@ |the type test for Fun[Int, Double] cannot be checked at runtime because its type arguments can't be determined from Exp[Int => Int] | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/enum-approx2.scala:7:13 ------------------------------------------ -7 | case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // warn: unchecked - | ^ - |the type test for Exp[Int => String] cannot be checked at runtime because its type arguments can't be determined from Exp[Int => Int] - | - | longer explanation available when compiling with `-explain` diff --git a/tests/warn/enum-approx2.scala b/tests/warn/enum-approx2.scala index 2c2563a8b2f1..38a78cd6a5e9 100644 --- a/tests/warn/enum-approx2.scala +++ b/tests/warn/enum-approx2.scala @@ -4,7 +4,7 @@ case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] class Test { def eval(e: Fun[Int, Int]) = e match { case Fun(x: Fun[Int, Double]) => ??? // warn: unchecked - case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // warn: unchecked + case Fun(x: Exp[Int => String]) => ??? // warn: unreachable // also: unchecked (hidden) case _ => // warn: unreachable-only-null } } diff --git a/tests/warn/i16451.check b/tests/warn/i16451.check index 2bc469af480b..a966b5c85be4 100644 --- a/tests/warn/i16451.check +++ b/tests/warn/i16451.check @@ -1,9 +1,9 @@ -- [E030] Match case Unreachable Warning: tests/warn/i16451.scala:14:9 ------------------------------------------------- -14 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked +14 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Unreachable case -- [E030] Match case Unreachable Warning: tests/warn/i16451.scala:22:9 ------------------------------------------------- -22 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked +22 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Unreachable case -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:13:9 ------------------------------------------------ @@ -12,24 +12,12 @@ |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:14:9 ------------------------------------------------ -14 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Green : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] - | - | longer explanation available when compiling with `-explain` -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:21:9 ------------------------------------------------ 21 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked | ^ |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:22:9 ------------------------------------------------ -22 | case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked - | ^ - |the type test for Wrapper[(Color.Green : Color)] cannot be checked at runtime because its type arguments can't be determined from Any - | - | longer explanation available when compiling with `-explain` -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:25:9 ------------------------------------------------ 25 | case x: Wrapper[Color.Red.type] => Some(x) // error: unreachable // error: unchecked | ^ diff --git a/tests/warn/i16451.scala b/tests/warn/i16451.scala index b11def9ec2ba..1a83d56366f6 100644 --- a/tests/warn/i16451.scala +++ b/tests/warn/i16451.scala @@ -11,7 +11,7 @@ case class Wrapper[A](value: A) object Test: def test_correct(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked + case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) def test_different(x: Wrapper[Color]): Option[Wrapper[Color]] = x match case x @ Wrapper(_: Color.Red.type) => Some(x) @@ -19,7 +19,7 @@ object Test: def test_any(x: Any): Option[Wrapper[Color.Red.type]] = x match case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked - case x: Wrapper[Color.Green.type] => None // warn: unreachable // warn: unchecked + case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) def test_wrong(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match case x: Wrapper[Color.Red.type] => Some(x) // error: unreachable // error: unchecked From 53d2e891897169adf14aeeb3c0037dcb81c4ac14 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 14 Aug 2023 13:06:21 +0100 Subject: [PATCH 034/134] Handle opaque aliases of arrays in Space erase [Cherry-picked 4421d12c70218aea86ae3bad6abf990756e7815b] --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 3 ++- tests/pos/i18364.Tup.scala | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18364.Tup.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 90039422548e..b6bf8c550a57 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -458,6 +458,7 @@ object SpaceEngine { * * @param inArray whether `tp` is a type argument to `Array` * @param isValue whether `tp` is the type which match against values + * @param isTyped whether `tp` is the type from a `Typed` tree * * If `isValue` is true, then pattern-bound symbols are erased to its upper bound. * This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala. @@ -468,7 +469,7 @@ object SpaceEngine { WildcardType case tp @ AppliedType(tycon, args) => - val inArray = tycon.isRef(defn.ArrayClass) + val inArray = tycon.isRef(defn.ArrayClass) || tp.translucentSuperType.isRef(defn.ArrayClass) val args2 = if isTyped && !inArray then args.map(_ => WildcardType) else args.map(arg => erase(arg, inArray = inArray, isValue = false)) diff --git a/tests/pos/i18364.Tup.scala b/tests/pos/i18364.Tup.scala new file mode 100644 index 000000000000..806342934e67 --- /dev/null +++ b/tests/pos/i18364.Tup.scala @@ -0,0 +1,10 @@ +// Capturing the regression will implementing the fix for i18364 +// That broke in CI, "case _" "Unreachable case except for null" +// Because IArray is an opaque alias of Array +object Tup: + /** Convert an immutable array into a tuple of unknown arity and types */ + def fromIArray[T](xs: IArray[T]): Tuple = + val xs2: IArray[Object] = xs match + case xs: IArray[Object] @unchecked => xs + case _ => xs.map(_.asInstanceOf[Object]) + runtime.Tuples.fromIArray(xs2) From dcbc2914be650d4ce4558ae17b801ef1a3ad5ad3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 15 Aug 2023 17:55:01 +0100 Subject: [PATCH 035/134] Vulpix: implement "// warn" tags Also, show compilation errors, if there are any, for a warn test. [Cherry-picked 3686ba06164d4db25cc12b2d688ea680f8864fab] --- .../tools/dotc/reporting/TestReporter.scala | 16 +++-- .../dotty/tools/vulpix/ParallelTesting.scala | 68 +++++++++++++++++++ tests/warn/i11178.scala | 11 ++- tests/warn/i16451.check | 8 +-- tests/warn/i16451.scala | 8 +-- tests/warn/i5826.check | 18 ++--- tests/warn/i5826.scala | 9 ++- tests/warn/i8932.scala | 4 +- .../warn/suppressed-type-test-warnings.scala | 8 +-- 9 files changed, 105 insertions(+), 45 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala index 940fc875a021..c0ece68e3b46 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala @@ -18,11 +18,12 @@ import interfaces.Diagnostic.{ERROR, WARNING} import scala.io.Codec -class TestReporter protected (outWriter: PrintWriter, filePrintln: String => Unit, logLevel: Int) +class TestReporter protected (outWriter: PrintWriter, logLevel: Int) extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with MessageRendering { - protected final val _errorBuf = mutable.ArrayBuffer.empty[Diagnostic] - final def errors: Iterator[Diagnostic] = _errorBuf.iterator + protected final val _diagnosticBuf = mutable.ArrayBuffer.empty[Diagnostic] + final def diagnostics: Iterator[Diagnostic] = _diagnosticBuf.iterator + final def errors: Iterator[Diagnostic] = diagnostics.filter(_.level >= ERROR) protected final val _messageBuf = mutable.ArrayBuffer.empty[String] final def messages: Iterator[String] = _messageBuf.iterator @@ -79,8 +80,9 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M case _ => "" } - if dia.level >= ERROR then _errorBuf.append(dia) - if dia.level >= WARNING then _consoleReporter.doReport(dia) + if dia.level >= WARNING then + _diagnosticBuf.append(dia) + _consoleReporter.doReport(dia) printMessageAndPos(dia, extra) } } @@ -125,10 +127,10 @@ object TestReporter { } def reporter(ps: PrintStream, logLevel: Int): TestReporter = - new TestReporter(new PrintWriter(ps, true), logPrintln, logLevel) + new TestReporter(new PrintWriter(ps, true), logLevel) def simplifiedReporter(writer: PrintWriter): TestReporter = { - val rep = new TestReporter(writer, logPrintln, WARNING) { + val rep = new TestReporter(writer, WARNING) { /** Prints the message with the given position indication in a simplified manner */ override def printMessageAndPos(dia: Diagnostic, extra: String)(using Context): Unit = { def report() = { diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index bcf17c37fa0b..4e6fe67aec37 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -729,6 +729,74 @@ trait ParallelTesting extends RunnerOrchestration { self => override def onSuccess(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable): Unit = diffCheckfile(testSource, reporters, logger) + override def maybeFailureMessage(testSource: TestSource, reporters: Seq[TestReporter]): Option[String] = + lazy val (map, expCount) = getWarnMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq) + lazy val obtCount = reporters.foldLeft(0)(_ + _.warningCount) + lazy val (expected, unexpected) = getMissingExpectedWarnings(map, reporters.iterator.flatMap(_.diagnostics)) + def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty + def showDiagnostics = "-> following the diagnostics:\n" + + reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "") + Option: + if reporters.exists(_.compilerCrashed) then s"Compiler crashed when compiling: ${testSource.title}" + else if reporters.exists(_.errorCount > 0) then + s"""Compilation failed for: ${testSource.title} + |$showDiagnostics + |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") + else if obtCount == 0 then s"\nNo warnings found when compiling warn test $testSource" + else if expCount == 0 then s"\nNo warning expected/defined in $testSource -- use // warn" + else if expCount != obtCount then + s"""|Wrong number of warnings encountered when compiling $testSource + |expected: $expCount, actual: $obtCount + |${expected.mkString("Unfulfilled expectations:\n", "\n", "")} + |${unexpected.mkString("Unexpected warnings:\n", "\n", "")} + |$showDiagnostics + |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") + else if hasMissingAnnotations then s"\nWarnings found on incorrect row numbers when compiling $testSource\n$showDiagnostics" + else if !map.isEmpty then s"\nExpected warnings(s) have {=}: $map" + else null + end maybeFailureMessage + + def getWarnMapAndExpectedCount(files: Seq[JFile]): (HashMap[String, Integer], Int) = + val comment = raw"//( *)warn".r + val map = new HashMap[String, Integer]() + var count = 0 + def bump(key: String): Unit = + map.get(key) match + case null => map.put(key, 1) + case n => map.put(key, n+1) + count += 1 + files.filter(isSourceFile).foreach { file => + Using(Source.fromFile(file, StandardCharsets.UTF_8.name)) { source => + source.getLines.zipWithIndex.foreach { case (line, lineNbr) => + comment.findAllMatchIn(line).foreach { _ => + bump(s"${file.getPath}:${lineNbr+1}") + } + } + }.get + } + (map, count) + + def getMissingExpectedWarnings(map: HashMap[String, Integer], reporterWarnings: Iterator[Diagnostic]): (List[String], List[String]) = + val unexpected, unpositioned = ListBuffer.empty[String] + def relativize(path: String): String = path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator) + def seenAt(key: String): Boolean = + map.get(key) match + case null => false + case 1 => map.remove(key) ; true + case n => map.put(key, n - 1) ; true + def sawDiagnostic(d: Diagnostic): Unit = + val srcpos = d.pos.nonInlined + if srcpos.exists then + val key = s"${relativize(srcpos.source.file.toString())}:${srcpos.line + 1}" + if !seenAt(key) then unexpected += key + else + unpositioned += relativize(srcpos.source.file.toString()) + + reporterWarnings.foreach(sawDiagnostic) + + (map.asScala.keys.toList, (unexpected ++ unpositioned).toList) + end getMissingExpectedWarnings + private final class RewriteTest(testSources: List[TestSource], checkFiles: Map[JFile, JFile], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting) extends Test(testSources, times, threadLimit, suppressAllOutput) { private def verifyOutput(testSource: TestSource, reporters: Seq[TestReporter], logger: LoggedRunnable) = { diff --git a/tests/warn/i11178.scala b/tests/warn/i11178.scala index 47e8b4c3acab..a59b899be365 100644 --- a/tests/warn/i11178.scala +++ b/tests/warn/i11178.scala @@ -3,7 +3,7 @@ case class Foo[+S](s: S) extends Box[S] def unwrap2[A](b: Box[A]): A = b match - case _: Foo[Int] => 0 // error + case _: Foo[Int] => 0 // warn object Test1 { // Invariant case, OK @@ -11,8 +11,7 @@ object Test1 { def test[A](bar: Bar[A]) = bar match { - case _: Bar[Boolean] => ??? // error - case _ => ??? + case _: Bar[Boolean] => ??? // warn } } @@ -22,8 +21,7 @@ object Test2 { def test[A](bar: Bar[A]) = bar match { - case _: Bar[Boolean] => ??? // error - case _ => ??? + case _: Bar[Boolean] => ??? // warn } } @@ -33,7 +31,6 @@ object Test3 { def test[A](bar: Bar[A]) = bar match { - case _: Bar[Boolean] => ??? // error - case _ => ??? + case _: Bar[Boolean] => ??? // warn } } diff --git a/tests/warn/i16451.check b/tests/warn/i16451.check index a966b5c85be4..09c2a7df8179 100644 --- a/tests/warn/i16451.check +++ b/tests/warn/i16451.check @@ -19,25 +19,25 @@ | | longer explanation available when compiling with `-explain` -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:25:9 ------------------------------------------------ -25 | case x: Wrapper[Color.Red.type] => Some(x) // error: unreachable // error: unchecked +25 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked | ^ |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] | | longer explanation available when compiling with `-explain` -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:29:9 ------------------------------------------------ -29 | case x: Wrapper[Color.Red.type] => Some(x) +29 | case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked | ^ |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1 | | longer explanation available when compiling with `-explain` -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:34:11 ----------------------------------------------- -34 | case x: Wrapper[Color.Red.type] => x +34 | case x: Wrapper[Color.Red.type] => x // warn: unchecked | ^ |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] | | longer explanation available when compiling with `-explain` -- [E092] Pattern Match Unchecked Warning: tests/warn/i16451.scala:39:11 ----------------------------------------------- -39 | case x: Wrapper[Color.Red.type] => x +39 | case x: Wrapper[Color.Red.type] => x // warn: unchecked | ^ |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] | diff --git a/tests/warn/i16451.scala b/tests/warn/i16451.scala index 1a83d56366f6..138af3632772 100644 --- a/tests/warn/i16451.scala +++ b/tests/warn/i16451.scala @@ -22,21 +22,21 @@ object Test: case x: Wrapper[Color.Green.type] => None // warn: unreachable // also: unchecked (hidden) def test_wrong(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) // error: unreachable // error: unchecked + case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked case null => None def t2[A1 <: Wrapper[Color]](x: A1): Option[Wrapper[Color.Red.type]] = x match - case x: Wrapper[Color.Red.type] => Some(x) + case x: Wrapper[Color.Red.type] => Some(x) // warn: unchecked case null => None def test_wrong_seq(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = xs.collect { - case x: Wrapper[Color.Red.type] => x + case x: Wrapper[Color.Red.type] => x // warn: unchecked } def test_wrong_seq2(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = xs.collect { x => x match - case x: Wrapper[Color.Red.type] => x + case x: Wrapper[Color.Red.type] => x // warn: unchecked } def main(args: Array[String]): Unit = diff --git a/tests/warn/i5826.check b/tests/warn/i5826.check index d29df1c8f34b..18ff50a933cb 100644 --- a/tests/warn/i5826.check +++ b/tests/warn/i5826.check @@ -1,9 +1,5 @@ --- [E121] Pattern Match Warning: tests/warn/i5826.scala:9:9 ------------------------------------------------------------ -9 | case _ => 0 // warn: unreachable-only-null - | ^ - | Unreachable case except for null (if this is intentional, consider writing case null => instead). -- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:3:9 -------------------------------------------------- -3 | case ls: List[Int] => ls.head // error, A = List[String] +3 | case ls: List[Int] => ls.head // warn: unchecked | ^ | the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from A | @@ -14,20 +10,20 @@ |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from List[String] | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:17:9 ------------------------------------------------- -17 | case ls: A[X] => 4 // error +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:16:9 ------------------------------------------------- +16 | case ls: A[X] => 4 // warn | ^ |the type test for Foo.this.A[X] cannot be checked at runtime because its type arguments can't be determined from Foo.this.B[X] | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:22:9 ------------------------------------------------- -22 | case ls: List[Int] => ls.head // error, List extends Int => T +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:21:9 ------------------------------------------------- +21 | case ls: List[Int] => ls.head // warn, List extends Int => T | ^ |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from A => Int | | longer explanation available when compiling with `-explain` --- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:28:54 ------------------------------------------------ -28 | def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // error +-- [E092] Pattern Match Unchecked Warning: tests/warn/i5826.scala:27:54 ------------------------------------------------ +27 | def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // warn | ^ |the type test for Foo.this.C[String] cannot be checked at runtime because its type arguments can't be determined from Foo.this.A[T] | diff --git a/tests/warn/i5826.scala b/tests/warn/i5826.scala index e7eda8826139..f54d6e58d033 100644 --- a/tests/warn/i5826.scala +++ b/tests/warn/i5826.scala @@ -1,12 +1,11 @@ class Foo { def test[A]: (List[Int] | A) => Int = { - case ls: List[Int] => ls.head // error, A = List[String] + case ls: List[Int] => ls.head // warn: unchecked case _ => 0 } def test2: List[Int] | List[String] => Int = { case ls: List[Int] => ls.head // warn: unchecked - case _ => 0 // warn: unreachable-only-null } trait A[T] @@ -14,18 +13,18 @@ class Foo { // suppose: class C extends A[Int] with B[String] def test3[X]: A[X] | B[X] => Int = { - case ls: A[X] => 4 // error + case ls: A[X] => 4 // warn case _ => 0 } def test4[A](x: List[Int] | (A => Int)) = x match { - case ls: List[Int] => ls.head // error, List extends Int => T + case ls: List[Int] => ls.head // warn, List extends Int => T case _ => 0 } final class C[T] extends A[T] - def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // error + def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // warn def test6[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[T]] diff --git a/tests/warn/i8932.scala b/tests/warn/i8932.scala index 84d2f7d4990a..95a4e86e9791 100644 --- a/tests/warn/i8932.scala +++ b/tests/warn/i8932.scala @@ -5,8 +5,8 @@ class Dummy extends Bar[Nothing] with Foo[String] def bugReport[A](foo: Foo[A]): Foo[A] = foo match { - case bar: Bar[A] => bar // error - case dummy: Dummy => ??? + case bar: Bar[A] => bar // warn: unchecked + case dummy: Dummy => ??? // warn: unreachable } def test = bugReport(new Dummy: Foo[String]) diff --git a/tests/warn/suppressed-type-test-warnings.scala b/tests/warn/suppressed-type-test-warnings.scala index 92d86b3307e5..c78e8e263153 100644 --- a/tests/warn/suppressed-type-test-warnings.scala +++ b/tests/warn/suppressed-type-test-warnings.scala @@ -11,19 +11,17 @@ object Test { } def err1[A, B](value: Foo[A, B], a: A => Int): B = value match { - case b: Bar[A] => // spurious // error + case b: Bar[A] => // spurious // warn b.x } def err2[A, B](value: Foo[A, B], a: A => Int): B = value match { - case b: Bar[B] => // spurious // error + case b: Bar[B] => // spurious // warn b.x - case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } def fail[A, B](value: Foo[A, B], a: A => Int): B = value match { - case b: Bar[Int] => // error + case b: Bar[Int] => // warn b.x - case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } } From 93f78de0ee1dd34766c18ab399e0722338802c83 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Fri, 1 Sep 2023 19:56:49 +0200 Subject: [PATCH 036/134] bugfix: Catch exception from the compiler for broken shadowed pickles Fixed in Metals: https://github.com/scalameta/metals/pull/5586 [Cherry-picked fa54869e035cda36cc252a8866f6342d85420444] --- .../main/dotty/tools/pc/CompilerSearchVisitor.scala | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala b/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala index 7920e67bc26a..2f7ee282450c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala @@ -22,6 +22,9 @@ class CompilerSearchVisitor( private def isAccessible(sym: Symbol): Boolean = try sym != NoSymbol && sym.isPublic && sym.isStatic catch + case err: AssertionError => + logger.log(Level.WARNING, err.getMessage()) + false case NonFatal(e) => reports.incognito.create( Report( @@ -64,8 +67,14 @@ class CompilerSearchVisitor( .stripSuffix("$") .split("\\$") - val added = toSymbols(pkg, innerPath.toList).filter(visitSymbol) + val added = + try toSymbols(pkg, innerPath.toList).filter(visitSymbol) + catch + case NonFatal(e) => + logger.log(Level.WARNING, e.getMessage(), e) + Nil added.size + end visitClassfile def visitWorkspaceSymbol( path: java.nio.file.Path, From da91db8f94ca5b643ce287de544ab2cbc8c139c2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 8 Aug 2023 09:10:16 +0200 Subject: [PATCH 037/134] Add missing span to synthesized product mirror Fixes #18353 [Cherry-picked ea5d7d37acea3610d4c2f7428fed5e12aa1114ac] --- .../dotty/tools/dotc/typer/Synthesizer.scala | 6 +- tests/pos-macros/i18353/Macro_1.scala | 64 +++++++++++++++++++ tests/pos-macros/i18353/Test_2.scala | 7 ++ 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 tests/pos-macros/i18353/Macro_1.scala create mode 100644 tests/pos-macros/i18353/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 85d1f084e6a0..1772155e54c7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -429,7 +429,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): if cls.useCompanionAsProductMirror then companionPath(mirroredType, span) else if defn.isTupleClass(cls) then newTupleMirror(typeElems.size) // TODO: cls == defn.PairClass when > 22 else anonymousMirror(monoType, MirrorImpl.OfProduct(pre), span) - withNoErrors(mirrorRef.cast(mirrorType)) + withNoErrors(mirrorRef.cast(mirrorType).withSpan(span)) end makeProductMirror MirrorSource.reduce(mirroredType) match @@ -442,12 +442,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, singleton.name) } val mirrorRef = New(defn.Mirror_SingletonProxyClass.typeRef, singletonPath :: Nil) - withNoErrors(mirrorRef.cast(mirrorType)) + withNoErrors(mirrorRef.cast(mirrorType).withSpan(span)) else val mirrorType = formal.constrained_& { mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, singleton.name) } - withNoErrors(singletonPath.cast(mirrorType)) + withNoErrors(singletonPath.cast(mirrorType).withSpan(span)) case MirrorSource.GenericTuple(tps) => val maxArity = Definitions.MaxTupleArity val arity = tps.size diff --git a/tests/pos-macros/i18353/Macro_1.scala b/tests/pos-macros/i18353/Macro_1.scala new file mode 100644 index 000000000000..d0f5dd84ea66 --- /dev/null +++ b/tests/pos-macros/i18353/Macro_1.scala @@ -0,0 +1,64 @@ +import scala.compiletime.* +import scala.deriving.* +import scala.quoted.* + +trait Getter[S, A]: + def view: S => A + +trait Lens[S, A] extends Getter[S, A]: + def set: S => A => S + +object Lens { + inline def apply[S, A](_view: S => A)(_set: S => A => S): Lens[S, A] = + new Lens[S, A]: + def view: S => A = _view + def set: S => A => S = _set + + inline given derived[T <: Product, A]: Lens[T, A] = ${ + ProductMacros.genLens[T, A] + } +} + +object ProductMacros { + private def indexOf[T: Type, A: Type](using Quotes): Int = + indexOf0[T, A](0) + + private def indexOf0[T: Type, A: Type](acc: Int)(using Quotes): Int = + Type.of[T] match + case '[EmptyTuple] => -1 + case '[A *: tpes] => acc + case '[tpe *: tpes] => indexOf0[tpes, A](acc + 1) + + def genLens[T <: Product: Type, A: Type](using + q: Quotes + ): Expr[Lens[T, A]] = { + import quotes.reflect.* + + Expr + .summon[Mirror.ProductOf[T]] + .map { + case '{ + $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes } + } => + val i = indexOf[elementTypes, A] + if i < 0 then + report.errorAndAbort(s"has no the field of ${Type.show[A]}") + else + val ii: Expr[Int] = Expr(i) + val view: Expr[T => A] = '{ t => + t.productElement($ii).asInstanceOf[A] + } + val set: Expr[T => A => T] = '{ t => a => + val arr = Tuple.fromProduct(t).toArray + arr($ii) = a.asInstanceOf[Object] + // Check-macros fails here probably + $m.fromTuple(Tuple.fromArray(arr).asInstanceOf[elementTypes]) + } + '{ Lens[T, A]($view)($set) } + } + .getOrElse( + report.errorAndAbort(s"${Type.show[T]} is not a product type") + ) + + } +} diff --git a/tests/pos-macros/i18353/Test_2.scala b/tests/pos-macros/i18353/Test_2.scala new file mode 100644 index 000000000000..08a79953097e --- /dev/null +++ b/tests/pos-macros/i18353/Test_2.scala @@ -0,0 +1,7 @@ +def Test = { + + type TupleConfig = (Int, String) + + val tConfig = (1, "string") + val fails = summon[Lens[TupleConfig, Int]].view(tConfig) +} From b64bde56638938ce147ed26d8ed07ab02422f84a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 31 Aug 2023 02:24:50 -0700 Subject: [PATCH 038/134] Tweak java getlitch not to skip zero [Cherry-picked 5f29c2178fc253a0fcfd8af0100e18ff434ea546] --- .../src/dotty/tools/dotc/parsing/JavaScanners.scala | 6 ++++-- tests/run/t12290.check | 8 ++++++++ tests/run/t12290/Test.scala | 12 ++++++++++++ tests/run/t12290/TextBlocks.java | 3 +++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala b/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala index d21d4b85b5df..6a1d5d8b216c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala @@ -439,6 +439,7 @@ object JavaScanners { } oct.asInstanceOf[Char] end octal + var skip = false def greatEscape: Char = nextChar() if '0' <= ch && ch <= '7' then octal @@ -455,11 +456,12 @@ object JavaScanners { case '\\' => '\\' case CR | LF if inTextBlock => if !scanOnly then nextChar() + skip = true 0 case _ => if !scanOnly then error("invalid escape character", charOffset - 1) ch - if x != 0 then nextChar() + if !skip then nextChar() x end greatEscape @@ -470,7 +472,7 @@ object JavaScanners { val res = ch nextChar() res - if c != 0 && !scanOnly then putChar(c) + if !skip && !scanOnly then putChar(c) end getlitch /** Read a triple-quote delimited text block, starting after the first three double quotes. diff --git a/tests/run/t12290.check b/tests/run/t12290.check index c6ce23a28ef2..f5367692b5f8 100644 --- a/tests/run/t12290.check +++ b/tests/run/t12290.check @@ -64,3 +64,11 @@ XY ==== X Y ==== +582059 +==== +00 +==== +2a +==== +c3bf +==== diff --git a/tests/run/t12290/Test.scala b/tests/run/t12290/Test.scala index e6c96573f032..7b0133e61c1f 100644 --- a/tests/run/t12290/Test.scala +++ b/tests/run/t12290/Test.scala @@ -30,4 +30,16 @@ object Test extends App { println("====") println(valueOf[TextBlocks.Octal.type]) println("====") + println(hexdump(valueOf[TextBlocks.Octal.type])) + println("====") + println(hexdump(valueOf[TextBlocks.Zero.type].toString)) + println("====") + println(hexdump(valueOf[TextBlocks.Magic.type].toString)) + println("====") + println(hexdump(valueOf[TextBlocks.Maxie.type].toString)) + println("====") } + +def hexdump(s: String) = s.getBytes(io.Codec.UTF8.charSet) // java.nio.charset.StandardCharsets.UTF_8 + .map(b => f"${b & 0xff}%02x") + .mkString diff --git a/tests/run/t12290/TextBlocks.java b/tests/run/t12290/TextBlocks.java index 6a827923a052..9dd34e1546a5 100644 --- a/tests/run/t12290/TextBlocks.java +++ b/tests/run/t12290/TextBlocks.java @@ -81,4 +81,7 @@ class TextBlocks { """; final static String Octal = "X\040Y"; + final static char Zero = '\0'; + final static char Magic = '\52'; + final static char Maxie = '\377'; } From a6e3a81aa3d5de6459a22fd610a2623cca884de8 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Fri, 8 Sep 2023 13:24:57 +0200 Subject: [PATCH 039/134] bugfix: highlight for enum type params [Cherry-picked e08c70e9332a4193f6267ce16741d9fee8b8105b] --- .../src/main/dotty/tools/pc/PcCollector.scala | 35 +++++++++++- .../highlight/DocumentHighlightSuite.scala | 57 ++++++++++++++++--- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala index 274b0704674d..fda62cbb25e4 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala @@ -111,6 +111,33 @@ abstract class PcCollector[T]( end adjust def symbolAlternatives(sym: Symbol) = + def member(parent: Symbol) = parent.info.member(sym.name).symbol + def primaryConstructorTypeParam(owner: Symbol) = + for + typeParams <- owner.primaryConstructor.paramSymss.headOption + param <- typeParams.find(_.name == sym.name) + if (param.isType) + yield param + def additionalForEnumTypeParam(enumClass: Symbol) = + if enumClass.is(Flags.Enum) then + val enumOwner = + if enumClass.is(Flags.Case) + then + Option.when(member(enumClass).is(Flags.Synthetic))( + enumClass.maybeOwner.companionClass + ) + else Some(enumClass) + enumOwner.toSet.flatMap { enumOwner => + val symsInEnumCases = enumOwner.children.toSet.flatMap(enumCase => + if member(enumCase).is(Flags.Synthetic) + then primaryConstructorTypeParam(enumCase) + else None + ) + val symsInEnumOwner = + primaryConstructorTypeParam(enumOwner).toSet + member(enumOwner) + symsInEnumCases ++ symsInEnumOwner + } + else Set.empty val all = if sym.is(Flags.ModuleClass) then Set(sym, sym.companionModule, sym.companionModule.companion) @@ -129,7 +156,11 @@ abstract class PcCollector[T]( ) ++ sym.allOverriddenSymbols.toSet // type used in primary constructor will not match the one used in the class else if sym.isTypeParam && sym.owner.isPrimaryConstructor then - Set(sym, sym.owner.owner.info.member(sym.name).symbol) + Set(sym, member(sym.maybeOwner.maybeOwner)) + ++ additionalForEnumTypeParam(sym.maybeOwner.maybeOwner) + else if sym.isTypeParam then + primaryConstructorTypeParam(sym.maybeOwner).toSet + ++ additionalForEnumTypeParam(sym.maybeOwner) + sym else Set(sym) all.filter(s => s != NoSymbol && !s.isError) end symbolAlternatives @@ -409,7 +440,7 @@ abstract class PcCollector[T]( * All select statements such as: * val a = hello.<> */ - case sel: Select + case sel: Select if sel.span.isCorrect && filter(sel) && !isForComprehensionMethod(sel) => occurrences + collect( diff --git a/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala index 4bf04ed1d0af..6ed5d6c636e3 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala @@ -758,8 +758,8 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: | } |}""".stripMargin ) - - @Test def `for-comp-map` = + + @Test def `for-comp-map` = check( """|object Main { | val x = List(1).<>(_ + 1) @@ -770,7 +770,7 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |""".stripMargin, ) - @Test def `for-comp-map1` = + @Test def `for-comp-map1` = check( """|object Main { | val x = List(1).<>(_ + 1) @@ -782,7 +782,7 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |""".stripMargin, ) - @Test def `for-comp-foreach` = + @Test def `for-comp-foreach` = check( """|object Main { | val x = List(1).<>(_ => ()) @@ -793,7 +793,7 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |""".stripMargin, ) - @Test def `for-comp-withFilter` = + @Test def `for-comp-withFilter` = check( """|object Main { | val x = List(1).<>(_ => true) @@ -805,7 +805,7 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |""".stripMargin, ) - @Test def `for-comp-withFilter1` = + @Test def `for-comp-withFilter1` = check( """|object Main { | val x = List(1).withFilter(_ => true).<>(_ + 1) @@ -817,7 +817,7 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |""".stripMargin, ) - @Test def `for-comp-flatMap1` = + @Test def `for-comp-flatMap1` = check( """|object Main { | val x = List(1).<>(_ => List(1)) @@ -830,7 +830,7 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |""".stripMargin, ) - @Test def `for-comp-flatMap2` = + @Test def `for-comp-flatMap2` = check( """|object Main { | val x = List(1).withFilter(_ => true).<>(_ => List(1)) @@ -1102,3 +1102,44 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: |val alpha = MyOption.<>(1) |""".stripMargin, ) + + @Test def `type-params-in-enum` = + check( + """|enum MyOption[+<>]: + | case MySome(value: <>) + | case MyNone + |""".stripMargin, + ) + + @Test def `type-params-in-enum2` = + check( + """|enum MyOption[+<>]: + | case MySome(value: <>) + | case MyNone + |""".stripMargin, + ) + + @Test def `type-params-in-enum3` = + check( + """|enum MyOption[<>](v: <>): + | def get: <> = ??? + | case MySome[AA](value: AA) extends MyOption[Int](1) + |""".stripMargin, + ) + + @Test def `type-params-in-enum4` = + check( + """|enum MyOption[+<>]: + | def get: <> = ??? + | case MySome(value: <>) + | case MyNone + |""".stripMargin, + ) + + @Test def `type-params-in-enum5` = + check( + """|enum MyOption[AA]: + | def get: AA = ??? + | case MySome[<>](value: <>) extends MyOption[Int] + |""".stripMargin, + ) From 4cae0d3ba6ca0903fef10be8a6c6e8f8615949cb Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Tue, 25 Apr 2023 13:56:14 +0200 Subject: [PATCH 040/134] bugfix: in semanticdb make synthetic apply disambiguator consistent with Scala 2 implementation [Cherry-picked 7a44b59d8f733356d8050e8d122e933b7cac2813] --- .../semanticdb/SemanticSymbolBuilder.scala | 2 +- .../semanticdb/expect/CaseClass.expect.scala | 7 +++ tests/semanticdb/expect/CaseClass.scala | 7 +++ tests/semanticdb/metac.expect | 53 +++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/semanticdb/expect/CaseClass.expect.scala create mode 100644 tests/semanticdb/expect/CaseClass.scala diff --git a/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala b/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala index c7b0dfd437db..0b92ebddb02c 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala @@ -84,7 +84,7 @@ class SemanticSymbolBuilder: else decls0 end decls - val alts = decls.filter(_.isOneOf(Method | Mutable)).toList.reverse + val alts = decls.filter(_.isOneOf(Method | Mutable)).toList.reverse.partition(!_.is(Synthetic)).toList.flatten def find(filter: Symbol => Boolean) = alts match case notSym :: rest if !filter(notSym) => val idx = rest.indexWhere(filter).ensuring(_ >= 0) diff --git a/tests/semanticdb/expect/CaseClass.expect.scala b/tests/semanticdb/expect/CaseClass.expect.scala new file mode 100644 index 000000000000..d0c509855c70 --- /dev/null +++ b/tests/semanticdb/expect/CaseClass.expect.scala @@ -0,0 +1,7 @@ +package caseclass + +case class CaseClass/*<-caseclass::CaseClass#*/(int1/*<-caseclass::CaseClass#int1.*/: Int/*->scala::Int#*/, int2/*<-caseclass::CaseClass#int2.*/: Int/*->scala::Int#*/) + +object CaseClass/*<-caseclass::CaseClass.*/: + def apply/*<-caseclass::CaseClass.apply().*/(int/*<-caseclass::CaseClass.apply().(int)*/: Int/*->scala::Int#*/): CaseClass/*->caseclass::CaseClass#*/ = CaseClass/*->caseclass::CaseClass.*/(int/*->caseclass::CaseClass.apply().(int)*/, 0) + def apply/*<-caseclass::CaseClass.apply(+1).*/(): CaseClass/*->caseclass::CaseClass#*/ = CaseClass/*->caseclass::CaseClass.*/(0, 0) diff --git a/tests/semanticdb/expect/CaseClass.scala b/tests/semanticdb/expect/CaseClass.scala new file mode 100644 index 000000000000..576678112353 --- /dev/null +++ b/tests/semanticdb/expect/CaseClass.scala @@ -0,0 +1,7 @@ +package caseclass + +case class CaseClass(int1: Int, int2: Int) + +object CaseClass: + def apply(int: Int): CaseClass = CaseClass(int, 0) + def apply(): CaseClass = CaseClass(0, 0) diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index e456b192e015..ee63e6c3d0df 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -477,6 +477,59 @@ Occurrences: [4:4..4:7): bar <- angiven/AnonymousGiven$package.bar(). [4:14..4:17): Foo -> angiven/Foo# +expect/CaseClass.scala +---------------------- + +Summary: +Schema => SemanticDB v4 +Uri => CaseClass.scala +Text => empty +Language => Scala +Symbols => 22 entries +Occurrences => 16 entries + +Symbols: +caseclass/CaseClass# => case class CaseClass extends Object with Product with Serializable { self: CaseClass => +8 decls } +caseclass/CaseClass#_1(). => method _1 => Int +caseclass/CaseClass#_2(). => method _2 => Int +caseclass/CaseClass#``(). => primary ctor (val param int1: Int, val param int2: Int): CaseClass +caseclass/CaseClass#``().(int1) => val param int1: Int +caseclass/CaseClass#``().(int2) => val param int2: Int +caseclass/CaseClass#copy$default$1(). => method copy$default$1 => Int @uncheckedVariance +caseclass/CaseClass#copy$default$2(). => method copy$default$2 => Int @uncheckedVariance +caseclass/CaseClass#copy(). => method copy (param int1: Int, param int2: Int): CaseClass +caseclass/CaseClass#copy().(int1) => param int1: Int +caseclass/CaseClass#copy().(int2) => param int2: Int +caseclass/CaseClass#int1. => val method int1 Int +caseclass/CaseClass#int2. => val method int2 Int +caseclass/CaseClass. => final object CaseClass extends Object { self: CaseClass.type => +5 decls } +caseclass/CaseClass.apply(). => method apply (param int: Int): CaseClass +caseclass/CaseClass.apply().(int) => param int: Int +caseclass/CaseClass.apply(+1). => method apply (): CaseClass +caseclass/CaseClass.apply(+2). => method apply (param int1: Int, param int2: Int): CaseClass +caseclass/CaseClass.apply(+2).(int1) => param int1: Int +caseclass/CaseClass.apply(+2).(int2) => param int2: Int +caseclass/CaseClass.unapply(). => method unapply (param x$1: CaseClass): CaseClass +caseclass/CaseClass.unapply().(x$1) => param x$1: CaseClass + +Occurrences: +[0:8..0:17): caseclass <- caseclass/ +[2:11..2:20): CaseClass <- caseclass/CaseClass# +[2:21..2:25): int1 <- caseclass/CaseClass#int1. +[2:27..2:30): Int -> scala/Int# +[2:32..2:36): int2 <- caseclass/CaseClass#int2. +[2:38..2:41): Int -> scala/Int# +[4:7..4:16): CaseClass <- caseclass/CaseClass. +[5:6..5:11): apply <- caseclass/CaseClass.apply(). +[5:12..5:15): int <- caseclass/CaseClass.apply().(int) +[5:17..5:20): Int -> scala/Int# +[5:23..5:32): CaseClass -> caseclass/CaseClass# +[5:35..5:44): CaseClass -> caseclass/CaseClass. +[5:45..5:48): int -> caseclass/CaseClass.apply().(int) +[6:6..6:11): apply <- caseclass/CaseClass.apply(+1). +[6:15..6:24): CaseClass -> caseclass/CaseClass# +[6:27..6:36): CaseClass -> caseclass/CaseClass. + expect/Classes.scala -------------------- From 7b3013e0f940924787e0ff874d39651dc8a71a4d Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 14 Jul 2023 12:33:39 +0200 Subject: [PATCH 041/134] reduce some warnings in semanticdb tests [Cherry-picked bef7a4a81a2af9420fbbe3819ad028ef004c7aee] --- tests/semanticdb/expect/Enums.expect.scala | 2 +- tests/semanticdb/expect/Enums.scala | 2 +- tests/semanticdb/expect/Givens.expect.scala | 3 +- tests/semanticdb/expect/Givens.scala | 3 +- tests/semanticdb/metac.expect | 49 +++++++++++++-------- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/tests/semanticdb/expect/Enums.expect.scala b/tests/semanticdb/expect/Enums.expect.scala index 3e1dc9087db7..404dd14b07d2 100644 --- a/tests/semanticdb/expect/Enums.expect.scala +++ b/tests/semanticdb/expect/Enums.expect.scala @@ -46,7 +46,7 @@ object Enums/*<-_empty_::Enums.*/: enum <:_empty_::Enums.`<:<`.Refl#[C]*/ <:_empty_::Enums.`<:<`#*/ C/*->_empty_::Enums.`<:<`.Refl#[C]*/) - object <:_empty_::Enums.`<:<`.`given_<:<_T_T`().[T]*/ <:_empty_::Enums.`<:<`#*/ T/*->_empty_::Enums.`<:<`.`given_<:<_T_T`().[T]*/) = Refl/*->_empty_::Enums.`<:<`.Refl.*/() extension [A/*<-_empty_::Enums.unwrap().[A]*/, B/*<-_empty_::Enums.unwrap().[B]*/](opt/*<-_empty_::Enums.unwrap().(opt)*/: Option/*->scala::Option#*/[A/*->_empty_::Enums.unwrap().[A]*/]) def unwrap/*<-_empty_::Enums.unwrap().*/(using ev/*<-_empty_::Enums.unwrap().(ev)*/: A/*->_empty_::Enums.unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/] = ev/*->_empty_::Enums.unwrap().(ev)*/ match diff --git a/tests/semanticdb/expect/Enums.scala b/tests/semanticdb/expect/Enums.scala index be7e2d6ce5cb..7647c9cafeb7 100644 --- a/tests/semanticdb/expect/Enums.scala +++ b/tests/semanticdb/expect/Enums.scala @@ -46,7 +46,7 @@ object Enums: enum <:<[-A, B]: case Refl[C]() extends (C <:< C) - object <:< : + object `<:<`: given [T]: (T <:< T) = Refl() extension [A, B](opt: Option[A]) def unwrap(using ev: A <:< Option[B]): Option[B] = ev match diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 5d7a9f5dc798..8ee82a419435 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -22,6 +22,7 @@ object Givens/*<-a::b::Givens.*/: def empty/*<-a::b::Givens.given_Monoid_String.empty().*/ = "" extension (x/*<-a::b::Givens.given_Monoid_String.combine().(x)*/: String/*->scala::Predef.String#*/) def combine/*<-a::b::Givens.given_Monoid_String.combine().*/(y/*<-a::b::Givens.given_Monoid_String.combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.combine().(y)*/ - inline given int2String/*<-a::b::Givens.int2String().*/: Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] = _.toString/*->scala::Any#toString().*/ + inline given int2String/*<-a::b::Givens.int2String().*/: Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] with + def apply/*<-a::b::Givens.int2String#apply().*/(x/*<-a::b::Givens.int2String#apply().(x)*/: Int/*->scala::Int#*/): String/*->scala::Predef.String#*/ = x/*->a::b::Givens.int2String#apply().(x)*/.toString/*->scala::Any#toString().*/ def foo/*<-a::b::Givens.foo().*/[A/*<-a::b::Givens.foo().[A]*/](using A/*<-a::b::Givens.foo().(A)*/: Monoid/*->a::b::Givens.Monoid#*/[A/*->a::b::Givens.foo().[A]*/]): A/*->a::b::Givens.foo().[A]*/ = A/*->a::b::Givens.foo().(A)*/.combine/*->a::b::Givens.Monoid#combine().*/(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/)(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/) diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 819d70cfadca..f0a56e624d5d 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -22,6 +22,7 @@ object Givens: def empty = "" extension (x: String) def combine(y: String) = x + y - inline given int2String: Conversion[Int, String] = _.toString + inline given int2String: Conversion[Int, String] with + def apply(x: Int): String = x.toString def foo[A](using A: Monoid[A]): A = A.combine(A.empty)(A.empty) diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index ee63e6c3d0df..00727163fda4 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -486,7 +486,7 @@ Uri => CaseClass.scala Text => empty Language => Scala Symbols => 22 entries -Occurrences => 16 entries +Occurrences => 17 entries Symbols: caseclass/CaseClass# => case class CaseClass extends Object with Product with Serializable { self: CaseClass => +8 decls } @@ -515,6 +515,7 @@ caseclass/CaseClass.unapply().(x$1) => param x$1: CaseClass Occurrences: [0:8..0:17): caseclass <- caseclass/ [2:11..2:20): CaseClass <- caseclass/CaseClass# +[2:20..2:20): <- caseclass/CaseClass#``(). [2:21..2:25): int1 <- caseclass/CaseClass#int1. [2:27..2:30): Int -> scala/Int# [2:32..2:36): int2 <- caseclass/CaseClass#int2. @@ -1285,7 +1286,7 @@ Occurrences: [46:28..46:29): C -> _empty_/Enums.`<:<`.Refl#[C] [46:30..46:33): <:< -> _empty_/Enums.`<:<`# [46:34..46:35): C -> _empty_/Enums.`<:<`.Refl#[C] -[48:9..48:12): <:< <- _empty_/Enums.`<:<`. +[48:10..48:13): <:< <- _empty_/Enums.`<:<`. [49:11..49:12): T <- _empty_/Enums.`<:<`.`given_<:<_T_T`().[T] [49:16..49:17): T -> _empty_/Enums.`<:<`.`given_<:<_T_T`().[T] [49:18..49:21): <:< -> _empty_/Enums.`<:<`# @@ -1636,12 +1637,12 @@ Schema => SemanticDB v4 Uri => Givens.scala Text => empty Language => Scala -Symbols => 29 entries -Occurrences => 66 entries +Symbols => 33 entries +Occurrences => 72 entries Synthetics => 3 entries Symbols: -a/b/Givens. => final object Givens extends Object { self: Givens.type => +12 decls } +a/b/Givens. => final object Givens extends Object { self: Givens.type => +13 decls } a/b/Givens.Monoid# => trait Monoid [typeparam A ] extends Object { self: Monoid[A] => +4 decls } a/b/Givens.Monoid#[A] => typeparam A a/b/Givens.Monoid#``(). => primary ctor [typeparam A ](): Monoid[A] @@ -1659,7 +1660,11 @@ a/b/Givens.given_Monoid_String.combine().(y) => param y: String a/b/Givens.given_Monoid_String.empty(). => method empty => String <: a/b/Givens.Monoid#empty(). a/b/Givens.goodbye1. => val method goodbye1 String a/b/Givens.hello1. => val method hello1 String -a/b/Givens.int2String(). => final implicit given inline macro int2String => Conversion[Int, String] +a/b/Givens.int2String# => implicit given class int2String extends Conversion[Int, String] { self: int2String => +2 decls } +a/b/Givens.int2String#``(). => primary ctor (): int2String +a/b/Givens.int2String#apply(). => method apply (param x: Int): String <: scala/Conversion#apply()., scala/Function1#apply(). +a/b/Givens.int2String#apply().(x) => param x: Int +a/b/Givens.int2String(). => final implicit given inline macro int2String => int2String a/b/Givens.sayGoodbye(). => method sayGoodbye [typeparam B ](param any: B): String a/b/Givens.sayGoodbye().(any) => param any: B a/b/Givens.sayGoodbye().[B] => typeparam B @@ -1723,21 +1728,27 @@ Occurrences: [22:55..22:56): y -> a/b/Givens.given_Monoid_String.combine().(y) [24:15..24:25): int2String <- a/b/Givens.int2String(). [24:27..24:37): Conversion -> scala/Conversion# +[24:27..24:27): <- a/b/Givens.int2String#``(). [24:38..24:41): Int -> scala/Int# [24:43..24:49): String -> scala/Predef.String# -[24:55..24:63): toString -> scala/Any#toString(). -[26:6..26:9): foo <- a/b/Givens.foo(). -[26:10..26:11): A <- a/b/Givens.foo().[A] -[26:19..26:20): A <- a/b/Givens.foo().(A) -[26:22..26:28): Monoid -> a/b/Givens.Monoid# -[26:29..26:30): A -> a/b/Givens.foo().[A] -[26:34..26:35): A -> a/b/Givens.foo().[A] -[26:38..26:39): A -> a/b/Givens.foo().(A) -[26:40..26:47): combine -> a/b/Givens.Monoid#combine(). -[26:48..26:49): A -> a/b/Givens.foo().(A) -[26:50..26:55): empty -> a/b/Givens.Monoid#empty(). -[26:57..26:58): A -> a/b/Givens.foo().(A) -[26:59..26:64): empty -> a/b/Givens.Monoid#empty(). +[25:8..25:13): apply <- a/b/Givens.int2String#apply(). +[25:14..25:15): x <- a/b/Givens.int2String#apply().(x) +[25:17..25:20): Int -> scala/Int# +[25:23..25:29): String -> scala/Predef.String# +[25:32..25:33): x -> a/b/Givens.int2String#apply().(x) +[25:34..25:42): toString -> scala/Any#toString(). +[27:6..27:9): foo <- a/b/Givens.foo(). +[27:10..27:11): A <- a/b/Givens.foo().[A] +[27:19..27:20): A <- a/b/Givens.foo().(A) +[27:22..27:28): Monoid -> a/b/Givens.Monoid# +[27:29..27:30): A -> a/b/Givens.foo().[A] +[27:34..27:35): A -> a/b/Givens.foo().[A] +[27:38..27:39): A -> a/b/Givens.foo().(A) +[27:40..27:47): combine -> a/b/Givens.Monoid#combine(). +[27:48..27:49): A -> a/b/Givens.foo().(A) +[27:50..27:55): empty -> a/b/Givens.Monoid#empty(). +[27:57..27:58): A -> a/b/Givens.foo().(A) +[27:59..27:64): empty -> a/b/Givens.Monoid#empty(). Synthetics: [12:17..12:25):sayHello => *[Int] From 10f2e718455013d7462b18592a18469f9dc25453 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Fri, 15 Sep 2023 10:48:14 +0200 Subject: [PATCH 042/134] Add missing span to extension method select [Cherry-picked fe4a6855efb1e59e050f3ebf00cfd91a32675eec] --- .../dotty/tools/dotc/inlines/Inliner.scala | 2 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../tools/dotc/typer/ErrorReporting.scala | 3 +- .../dotty/tools/dotc/typer/Implicits.scala | 12 +++--- .../tools/dotc/typer/ImportSuggestions.scala | 2 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 39 ++++++++++--------- .../src/dotty/tools/dotc/typer/Typer.scala | 10 ++--- .../tests/definition/PcDefinitionSuite.scala | 10 +++++ .../highlight/DocumentHighlightSuite.scala | 31 +++++++++++++++ .../tools/pc/tests/hover/HoverTypeSuite.scala | 12 ++++++ .../semanticdb/expect/Extension.expect.scala | 9 +++++ tests/semanticdb/expect/Extension.scala | 9 +++++ tests/semanticdb/metac.expect | 26 +++++++++++-- 13 files changed, 130 insertions(+), 37 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 29d6a5365978..66a83c66b5f8 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -771,7 +771,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { val locked = ctx.typerState.ownedVars - val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this)) + val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)) val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) val reducedProjection = reducer.reduceProjection(resNoReduce) if reducedProjection.isType then diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 19af6b02c25d..09daa429a08f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -959,7 +959,7 @@ trait Applications extends Compatibility { val resultType = if !originalResultType.isRef(defn.ObjectClass) then originalResultType else AvoidWildcardsMap()(proto.resultType.deepenProtoTrans) match - case SelectionProto(nme.asInstanceOf_, PolyProto(_, resTp), _, _) => resTp + case SelectionProto(nme.asInstanceOf_, PolyProto(_, resTp), _, _, _) => resTp case resTp if isFullyDefined(resTp, ForceDegree.all) => resTp case _ => defn.ObjectType val methType = MethodType(proto.typedArgs().map(_.tpe.widen), resultType) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 339d1f2f7bc6..2da934c6013a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -8,6 +8,7 @@ import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._ import Implicits._, Flags._, Constants.Constant import Trees._ import NameOps._ +import util.Spans.NoSpan import util.SrcPos import config.Feature import reporting._ @@ -266,7 +267,7 @@ object ErrorReporting { else val add = suggestImports( ViewProto(qualType.widen, - SelectionProto(tree.name, WildcardType, NoViewsAllowed, privateOK = false))) + SelectionProto(tree.name, WildcardType, NoViewsAllowed, privateOK = false, NoSpan))) if add.isEmpty then "" else ", but could be made available as an extension method." ++ add end selectErrorAddendum diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 29a35ccbdac0..3872d0dd79d0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -75,7 +75,7 @@ object Implicits: * method with the selecting name? False otherwise. */ def hasExtMethod(tp: Type, expected: Type)(using Context) = expected match - case selProto @ SelectionProto(selName: TermName, _, _, _) => + case selProto @ SelectionProto(selName: TermName, _, _, _, _) => tp.memberBasedOnFlags(selName, required = ExtensionMethod).exists case _ => false @@ -437,7 +437,7 @@ object Implicits: def clarify(tp: Type)(using Context): Type = tp final protected def qualify(using Context): String = expectedType match { - case SelectionProto(name, mproto, _, _) if !argument.isEmpty => + case SelectionProto(name, mproto, _, _, _) if !argument.isEmpty => i"provide an extension method `$name` on ${argument.tpe}" case NoType => if (argument.isEmpty) i"match expected type" @@ -842,8 +842,8 @@ trait Implicits: NoMatchingImplicitsFailure else { def adjust(to: Type) = to.stripTypeVar.widenExpr match { - case SelectionProto(name, memberProto, compat, true) => - SelectionProto(name, memberProto, compat, privateOK = false) + case SelectionProto(name, memberProto, compat, true, nameSpan) => + SelectionProto(name, memberProto, compat, privateOK = false, nameSpan) case tp => tp } @@ -1137,10 +1137,10 @@ trait Implicits: pt, locked) } pt match - case selProto @ SelectionProto(selName: TermName, mbrType, _, _) => + case selProto @ SelectionProto(selName: TermName, mbrType, _, _, nameSpan) => def tryExtension(using Context) = - extMethodApply(untpd.Select(untpdGenerated, selName), argument, mbrType) + extMethodApply(untpd.Select(untpdGenerated, selName).withSpan(nameSpan), argument, mbrType) def tryConversionForSelection(using Context) = val converted = tryConversion diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index 70addd442100..a21a94aab271 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -237,7 +237,7 @@ trait ImportSuggestions: // don't suggest things that are imported by default def extensionImports = pt match - case ViewProto(argType, SelectionProto(name: TermName, _, _, _)) => + case ViewProto(argType, SelectionProto(name: TermName, _, _, _, _)) => roots.flatMap(extensionMethod(_, name, argType)) case _ => Nil diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index bde279c582e6..31cca0301d7f 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -19,6 +19,7 @@ import util.SourceFile import TypeComparer.necessarySubType import scala.annotation.internal.sharable +import dotty.tools.dotc.util.Spans.{NoSpan, Span} object ProtoTypes { @@ -165,7 +166,7 @@ object ProtoTypes { * * [ ].name: proto */ - abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean) + abstract case class SelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span) extends CachedProxyType with ProtoType with ValueTypeOrProto { /** Is the set of members of this type unknown, in the sense that we @@ -230,9 +231,9 @@ object ProtoTypes { def underlying(using Context): Type = WildcardType - def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(using Context): SelectionProto = - if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this - else SelectionProto(name, memberProto, compat, privateOK) + def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, nameSpan: Span)(using Context): SelectionProto = + if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat) && (nameSpan == this.nameSpan)) this + else SelectionProto(name, memberProto, compat, privateOK, nameSpan) override def isErroneous(using Context): Boolean = memberProto.isErroneous @@ -240,14 +241,14 @@ object ProtoTypes { override def unusableForInference(using Context): Boolean = memberProto.unusableForInference - def map(tm: TypeMap)(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat) + def map(tm: TypeMap)(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat, nameSpan) def fold[T](x: T, ta: TypeAccumulator[T])(using Context): T = ta(x, memberProto) override def deepenProto(using Context): SelectionProto = - derivedSelectionProto(name, memberProto.deepenProto, compat) + derivedSelectionProto(name, memberProto.deepenProto, compat, nameSpan) override def deepenProtoTrans(using Context): SelectionProto = - derivedSelectionProto(name, memberProto.deepenProtoTrans, compat) + derivedSelectionProto(name, memberProto.deepenProtoTrans, compat, nameSpan) override def computeHash(bs: Hashable.Binders): Int = { val delta = (if (compat eq NoViewsAllowed) 1 else 0) | (if (privateOK) 2 else 0) @@ -268,12 +269,12 @@ object ProtoTypes { } } - class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean) - extends SelectionProto(name, memberProto, compat, privateOK) + class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span) + extends SelectionProto(name, memberProto, compat, privateOK, nameSpan) object SelectionProto { - def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean)(using Context): SelectionProto = { - val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK) + def apply(name: Name, memberProto: Type, compat: Compatibility, privateOK: Boolean, nameSpan: Span)(using Context): SelectionProto = { + val selproto = new CachedSelectionProto(name, memberProto, compat, privateOK, nameSpan) if (compat eq NoViewsAllowed) unique(selproto) else selproto } } @@ -281,11 +282,11 @@ object ProtoTypes { /** Create a selection proto-type, but only one level deep; * treat constructors specially */ - def shallowSelectionProto(name: Name, tp: Type, typer: Typer)(using Context): TermType = + def shallowSelectionProto(name: Name, tp: Type, typer: Typer, nameSpan: Span)(using Context): TermType = if (name.isConstructorName) WildcardType else tp match - case tp: UnapplyFunProto => new UnapplySelectionProto(name) - case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true) + case tp: UnapplyFunProto => new UnapplySelectionProto(name, nameSpan) + case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true, nameSpan) /** A prototype for expressions [] that are in some unspecified selection operation * @@ -295,12 +296,12 @@ object ProtoTypes { * operation is further selection. In this case, the expression need not be a value. * @see checkValue */ - @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true) + @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan) - @sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true) + @sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan) /** A prototype for selections in pattern constructors */ - class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed, true) + class UnapplySelectionProto(name: Name, nameSpan: Span) extends SelectionProto(name, WildcardType, NoViewsAllowed, true, nameSpan) trait ApplyingProto extends ProtoType // common trait of ViewProto and FunProto trait FunOrPolyProto extends ProtoType: // common trait of PolyProto and FunProto @@ -599,7 +600,7 @@ object ProtoTypes { def isMatchedBy(tp: Type, keepConstraint: Boolean)(using Context): Boolean = ctx.typer.isApplicableType(tp, argType :: Nil, resultType) || { resType match { - case selProto @ SelectionProto(selName: TermName, mbrType, _, _) => + case selProto @ SelectionProto(selName: TermName, mbrType, _, _, _) => ctx.typer.hasExtensionMethodNamed(tp, selName, argType, mbrType) //.reporting(i"has ext $tp $name $argType $mbrType: $result") case _ => @@ -921,7 +922,7 @@ object ProtoTypes { } approxOr case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed) + tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed, tp.nameSpan) case tp: ViewProto => tp.derivedViewProto( wildApprox(tp.argType, theMap, seen, internal), diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 34940d2f5277..c18a369207a5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -752,7 +752,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer record("typedSelect") def typeSelectOnTerm(using Context): Tree = - val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this)) + val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)) typedSelect(tree, pt, qual).withSpan(tree.span).computeNullable() def javaSelectOnType(qual: Tree)(using Context) = @@ -782,7 +782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tryAlternatively(typeSelectOnTerm)(fallBack) if (tree.qualifier.isType) { - val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this)) + val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)) assignType(cpy.Select(tree)(qual1, tree.name), qual1) } else if (ctx.isJava && tree.name.isTypeName) @@ -3442,7 +3442,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer then Some(adapt(tree, pt, locked)) else - val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false) + val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false, tree.nameSpan) if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then None else @@ -3467,7 +3467,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer (tree: untpd.Select, pt: Type, mbrProto: Type, qual: Tree, locked: TypeVars, compat: Compatibility, inSelect: Boolean) (using Context): Tree = - def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect) + def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect, tree.nameSpan) def tryExtension(using Context): Tree = val altImports = new mutable.ListBuffer[TermRef]() @@ -3897,7 +3897,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * function prototype `(...)R`. Otherwise `pt`. */ def ptWithoutRedundantApply: Type = pt.revealIgnored match - case SelectionProto(nme.apply, mpt, _, _) => + case SelectionProto(nme.apply, mpt, _, _, _) => mpt.revealIgnored match case fpt: FunProto => fpt case _ => pt diff --git a/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala index 4869cdf0fa3b..2b49d2db3f08 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala @@ -401,3 +401,13 @@ class PcDefinitionSuite extends BasePcDefinitionSuite: | |""".stripMargin ) + + @Test def `implicit-extension` = + check( + """|class MyIntOut(val value: Int) + |object MyIntOut: + | extension (i: MyIntOut) def <> = i.value % 2 == 1 + | + |val a = MyIntOut(1).un@@even + |""".stripMargin, + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala index 6ed5d6c636e3..0ed40c6c537f 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/highlight/DocumentHighlightSuite.scala @@ -1143,3 +1143,34 @@ class DocumentHighlightSuite extends BaseDocumentHighlightSuite: | case MySome[<>](value: <>) extends MyOption[Int] |""".stripMargin, ) + + @Test def `implicit-extension` = + check( + """|class MyIntOut(val value: Int) + |object MyIntOut: + | extension (i: MyIntOut) def <> = i.value % 2 == 1 + | + |val a = MyIntOut(1) + |val m = a.<> + |""".stripMargin, + ) + + @Test def `implicit-extension-2` = + check( + """|class MyIntOut(val value: Int) + |object MyIntOut: + | extension (i: MyIntOut) def <>(u: Int) = i.value % 2 == 1 + | + |val a = MyIntOut(1).<>(3) + |""".stripMargin, + ) + + @Test def `implicit-extension-infix` = + check( + """|class MyIntOut(val value: Int) + |object MyIntOut: + | extension (i: MyIntOut) def <<++>>(u: Int) = i.value + u + | + |val a = MyIntOut(1) <<+@@+>> 3 + |""".stripMargin, + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala index 12c3a7be584f..984066fb7803 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTypeSuite.scala @@ -355,3 +355,15 @@ class HoverTypeSuite extends BaseHoverSuite: """|val ddd: Int |""".stripMargin.hover, ) + + @Test def `infix-extension` = + check( + """|class MyIntOut(val value: Int) + |object MyIntOut: + | extension (i: MyIntOut) def uneven = i.value % 2 == 1 + | + |val a = MyIntOut(1).un@@even + |""".stripMargin, + """|extension (i: MyIntOut) def uneven: Boolean + |""".stripMargin.hover, + ) diff --git a/tests/semanticdb/expect/Extension.expect.scala b/tests/semanticdb/expect/Extension.expect.scala index b40e965d4885..f6f76b17b698 100644 --- a/tests/semanticdb/expect/Extension.expect.scala +++ b/tests/semanticdb/expect/Extension.expect.scala @@ -16,3 +16,12 @@ extension (s/*<-ext::Extension$package.readInto().(s)*/: String/*->scala::Predef trait Functor/*<-ext::Functor#*/[F/*<-ext::Functor#[F]*/[_]]: extension [T/*<-ext::Functor#map().[T]*/](t/*<-ext::Functor#map().(t)*/: F/*->ext::Functor#[F]*/[T/*->ext::Functor#map().[T]*/]) def map/*<-ext::Functor#map().*/[U/*<-ext::Functor#map().[U]*/](f/*<-ext::Functor#map().(f)*/: T/*->ext::Functor#map().[T]*/ => U/*->ext::Functor#map().[U]*/): F/*->ext::Functor#[F]*/[U/*->ext::Functor#map().[U]*/] + +opaque type Deck/*<-ext::Extension$package.Deck#*/ = Long/*->scala::Long#*/ +object Deck/*<-ext::Extension$package.Deck.*/: + extension (data/*<-ext::Extension$package.Deck.fooSize().(data)*/: Deck/*->ext::Extension$package.Deck#*/) + def fooSize/*<-ext::Extension$package.Deck.fooSize().*/: Int/*->scala::Int#*/ = ???/*->scala::Predef.`???`().*/ + +object DeckUsage/*<-ext::DeckUsage.*/: + val deck/*<-ext::DeckUsage.deck.*/: Deck/*->ext::Extension$package.Deck#*/ = ???/*->scala::Predef.`???`().*/ + deck/*->ext::DeckUsage.deck.*/.fooSize/*->ext::Extension$package.Deck.fooSize().*/ diff --git a/tests/semanticdb/expect/Extension.scala b/tests/semanticdb/expect/Extension.scala index c204b1ff7fcc..76a012e4b758 100644 --- a/tests/semanticdb/expect/Extension.scala +++ b/tests/semanticdb/expect/Extension.scala @@ -16,3 +16,12 @@ extension (s: String) trait Functor[F[_]]: extension [T](t: F[T]) def map[U](f: T => U): F[U] + +opaque type Deck = Long +object Deck: + extension (data: Deck) + def fooSize: Int = ??? + +object DeckUsage: + val deck: Deck = ??? + deck.fooSize diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 00727163fda4..d8aa1e39abef 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1450,12 +1450,18 @@ Schema => SemanticDB v4 Uri => Extension.scala Text => empty Language => Scala -Symbols => 26 entries -Occurrences => 52 entries +Symbols => 32 entries +Occurrences => 66 entries Synthetics => 1 entries Symbols: -ext/Extension$package. => final package object ext extends Object { self: ext.type => +6 decls } +ext/DeckUsage. => final object DeckUsage extends Object { self: DeckUsage.type => +2 decls } +ext/DeckUsage.deck. => val method deck Deck +ext/Extension$package. => final package object ext extends Object { self: ext.type { opaque type Deck } => +9 decls } +ext/Extension$package.Deck# => opaque type Deck +ext/Extension$package.Deck. => final object Deck extends Object { self: Deck.type => +2 decls } +ext/Extension$package.Deck.fooSize(). => method fooSize (param data: Deck): Int +ext/Extension$package.Deck.fooSize().(data) => param data: Deck ext/Extension$package.`#*#`(). => method #*# (param s: String)(param i: Int): Tuple2[String, Int] ext/Extension$package.`#*#`().(i) => param i: Int ext/Extension$package.`#*#`().(s) => param s: String @@ -1535,6 +1541,20 @@ Occurrences: [17:44..17:45): U -> ext/Functor#map().[U] [17:48..17:49): F -> ext/Functor#[F] [17:50..17:51): U -> ext/Functor#map().[U] +[19:12..19:16): Deck <- ext/Extension$package.Deck# +[19:19..19:23): Long -> scala/Long# +[20:7..20:11): Deck <- ext/Extension$package.Deck. +[21:13..21:17): data <- ext/Extension$package.Deck.fooSize().(data) +[21:19..21:23): Deck -> ext/Extension$package.Deck# +[22:8..22:15): fooSize <- ext/Extension$package.Deck.fooSize(). +[22:17..22:20): Int -> scala/Int# +[22:23..22:26): ??? -> scala/Predef.`???`(). +[24:7..24:16): DeckUsage <- ext/DeckUsage. +[25:6..25:10): deck <- ext/DeckUsage.deck. +[25:12..25:16): Deck -> ext/Extension$package.Deck# +[25:19..25:22): ??? -> scala/Predef.`???`(). +[26:2..26:6): deck -> ext/DeckUsage.deck. +[26:7..26:14): fooSize -> ext/Extension$package.Deck.fooSize(). Synthetics: [14:46..14:61):summon[Read[T]] => *(x$2) From eb19030f6d9864e75790108eaf60292d55640be3 Mon Sep 17 00:00:00 2001 From: Jakub Ciesluk <323892@uwr.edu.pl> Date: Thu, 21 Sep 2023 10:50:44 +0200 Subject: [PATCH 043/134] bugfix: Incorrect semanticdb span on Selectable Semanticdb range on selectDynamic in `foo.bar` previously contained `.bar` instead of `bar` [Cherry-picked 5df5855ac5099363583e43751e16cd1808880c78] --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 +- .../dotc/semanticdb/ExtractSemanticDB.scala | 11 +-- tests/semanticdb/expect/Givens.expect.scala | 6 +- .../expect/ImplicitConversion.expect.scala | 6 +- .../expect/StructuralTypes.expect.scala | 22 +++++ tests/semanticdb/expect/StructuralTypes.scala | 22 +++++ tests/semanticdb/metac.expect | 87 ++++++++++++++++--- 7 files changed, 132 insertions(+), 24 deletions(-) create mode 100644 tests/semanticdb/expect/StructuralTypes.expect.scala create mode 100644 tests/semanticdb/expect/StructuralTypes.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 8d19dfdf6743..c202f7ccb523 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -455,7 +455,7 @@ object Trees { val point = span.point if name.toTermName == nme.ERROR then Span(point) - else if qualifier.span.start > span.start then // right associative + else if qualifier.span.start > span.point then // right associative val realName = name.stripModuleClassSuffix.lastPart Span(span.start, span.start + realName.length, point) else diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 5cff105cbaa4..e364bf15ca13 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -392,15 +392,12 @@ class ExtractSemanticDB extends Phase: }).toMap end findGetters - private def selectSpan(tree: Select) = + private def selectSpan(tree: Select)(using Context) = val end = tree.span.end val limit = tree.qualifier.span.end - val start = - if limit < end then - val len = tree.name.toString.length - if tree.source.content()(end - 1) == '`' then end - len - 2 else end - len - else limit - Span(start max limit, end) + if limit < end then + tree.nameSpan + else Span(limit, end) extension (span: Span) private def hasLength: Boolean = span.exists && !span.isZeroExtent diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 8ee82a419435..8cd1ee287096 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -4,11 +4,11 @@ package b object Givens/*<-a::b::Givens.*/: extension [A/*<-a::b::Givens.sayHello().[A]*/](any/*<-a::b::Givens.sayHello().(any)*/: A/*->a::b::Givens.sayHello().[A]*/) - def sayHello/*<-a::b::Givens.sayHello().*/ = s"Hello, I am $any/*->a::b::Givens.sayHello().(any)*/"/*->scala::StringContext#s().*/ + def sayHello/*<-a::b::Givens.sayHello().*/ = s/*->scala::StringContext#s().*/"Hello, I am $any/*->a::b::Givens.sayHello().(any)*/" extension [B/*<-a::b::Givens.sayGoodbye().[B]*//*<-a::b::Givens.saySoLong().[B]*/](any/*<-a::b::Givens.sayGoodbye().(any)*//*<-a::b::Givens.saySoLong().(any)*/: B/*->a::b::Givens.sayGoodbye().[B]*//*->a::b::Givens.saySoLong().[B]*/) - def sayGoodbye/*<-a::b::Givens.sayGoodbye().*/ = s"Goodbye, from $any/*->a::b::Givens.sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ - def saySoLong/*<-a::b::Givens.saySoLong().*/ = s"So Long, from $any/*->a::b::Givens.saySoLong().(any)*/"/*->scala::StringContext#s().*/ + def sayGoodbye/*<-a::b::Givens.sayGoodbye().*/ = s/*->scala::StringContext#s().*/"Goodbye, from $any/*->a::b::Givens.sayGoodbye().(any)*/" + def saySoLong/*<-a::b::Givens.saySoLong().*/ = s/*->scala::StringContext#s().*/"So Long, from $any/*->a::b::Givens.saySoLong().(any)*/" val hello1/*<-a::b::Givens.hello1.*/ = 1.sayHello/*->a::b::Givens.sayHello().*/ val goodbye1/*<-a::b::Givens.goodbye1.*/ = 1.sayGoodbye/*->a::b::Givens.sayGoodbye().*/ diff --git a/tests/semanticdb/expect/ImplicitConversion.expect.scala b/tests/semanticdb/expect/ImplicitConversion.expect.scala index 635ffb4d94c9..7c1708ee7617 100644 --- a/tests/semanticdb/expect/ImplicitConversion.expect.scala +++ b/tests/semanticdb/expect/ImplicitConversion.expect.scala @@ -21,10 +21,10 @@ class ImplicitConversion/*<-example::ImplicitConversion#*/ { val x/*<-example::ImplicitConversion#x.*/: Int/*->scala::Int#*/ = message/*->example::ImplicitConversion#message.*/ // interpolators - s"Hello $message/*->example::ImplicitConversion#message.*/ $number/*->example::ImplicitConversion#number.*/"/*->scala::StringContext#s().*/ - s"""Hello + s/*->scala::StringContext#s().*/"Hello $message/*->example::ImplicitConversion#message.*/ $number/*->example::ImplicitConversion#number.*/" + s/*->scala::StringContext#s().*/"""Hello |$message/*->example::ImplicitConversion#message.*/ - |$number/*->example::ImplicitConversion#number.*/"""/*->scala::StringContext#s().*/.stripMargin/*->scala::collection::StringOps#stripMargin(+1).*/ + |$number/*->example::ImplicitConversion#number.*/""".stripMargin/*->scala::collection::StringOps#stripMargin(+1).*/ val a/*<-example::ImplicitConversion#a.*/: Int/*->scala::Int#*/ = char/*->example::ImplicitConversion#char.*/ val b/*<-example::ImplicitConversion#b.*/: Long/*->scala::Long#*/ = char/*->example::ImplicitConversion#char.*/ diff --git a/tests/semanticdb/expect/StructuralTypes.expect.scala b/tests/semanticdb/expect/StructuralTypes.expect.scala new file mode 100644 index 000000000000..96c7181d6f10 --- /dev/null +++ b/tests/semanticdb/expect/StructuralTypes.expect.scala @@ -0,0 +1,22 @@ +package example + +import reflect.Selectable/*->scala::reflect::Selectable.*/.reflectiveSelectable/*->scala::reflect::Selectable.reflectiveSelectable().*/ + +object StructuralTypes/*<-example::StructuralTypes.*/: + type User/*<-example::StructuralTypes.User#*/ = { + def name/*<-local0*/: String/*->scala::Predef.String#*/ + def age/*<-local1*/: Int/*->scala::Int#*/ + def foo/*<-local3*/(x/*<-local2*/: Int/*->scala::Int#*/): Int/*->scala::Int#*/ + } + + val user/*<-example::StructuralTypes.user.*/ = null.asInstanceOf/*->scala::Any#asInstanceOf().*/[User/*->example::StructuralTypes.User#*/] + user/*->example::StructuralTypes.user.*/.name/*->scala::reflect::Selectable#selectDynamic().*/ + user/*->example::StructuralTypes.user.*/.age/*->scala::reflect::Selectable#selectDynamic().*/ + val fooBar/*<-example::StructuralTypes.fooBar.*/ = user/*->example::StructuralTypes.user.*/ foo/*->scala::reflect::Selectable#applyDynamic().*/ 123 + + val V/*<-example::StructuralTypes.V.*/: Object/*->java::lang::Object#*/ { + def scalameta/*<-local4*/: String/*->scala::Predef.String#*/ + } = /*<-local6*/new: + def scalameta/*<-local5*/ = "4.0" + V/*->example::StructuralTypes.V.*/.scalameta/*->scala::reflect::Selectable#selectDynamic().*/ +end StructuralTypes/*->example::StructuralTypes.*/ \ No newline at end of file diff --git a/tests/semanticdb/expect/StructuralTypes.scala b/tests/semanticdb/expect/StructuralTypes.scala new file mode 100644 index 000000000000..5d10dbe67224 --- /dev/null +++ b/tests/semanticdb/expect/StructuralTypes.scala @@ -0,0 +1,22 @@ +package example + +import reflect.Selectable.reflectiveSelectable + +object StructuralTypes: + type User = { + def name: String + def age: Int + def foo(x: Int): Int + } + + val user = null.asInstanceOf[User] + user.name + user.age + val fooBar = user foo 123 + + val V: Object { + def scalameta: String + } = new: + def scalameta = "4.0" + V.scalameta +end StructuralTypes \ No newline at end of file diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index d8aa1e39abef..d2220e2af17a 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -193,21 +193,21 @@ Occurrences: [27:6..27:9): s1x <- advanced/Test.s1x. [27:12..27:13): s -> advanced/Test.s. [27:14..27:16): s1 -> advanced/Structural#s1(). -[27:16..27:18): .x -> scala/reflect/Selectable#selectDynamic(). +[27:17..27:18): x -> scala/reflect/Selectable#selectDynamic(). [28:6..28:8): s2 <- advanced/Test.s2. [28:11..28:12): s -> advanced/Test.s. [28:13..28:15): s2 -> advanced/Structural#s2(). [29:6..29:9): s2x <- advanced/Test.s2x. [29:12..29:13): s -> advanced/Test.s. [29:14..29:16): s2 -> advanced/Structural#s2(). -[29:16..29:18): .x -> scala/reflect/Selectable#selectDynamic(). +[29:17..29:18): x -> scala/reflect/Selectable#selectDynamic(). [30:6..30:8): s3 <- advanced/Test.s3. [30:11..30:12): s -> advanced/Test.s. [30:13..30:15): s3 -> advanced/Structural#s3(). [31:6..31:9): s3x <- advanced/Test.s3x. [31:12..31:13): s -> advanced/Test.s. [31:14..31:16): s3 -> advanced/Structural#s3(). -[31:16..31:18): .m -> scala/reflect/Selectable#applyDynamic(). +[31:17..31:18): m -> scala/reflect/Selectable#applyDynamic(). [31:19..31:22): ??? -> scala/Predef.`???`(). [33:6..33:7): e <- advanced/Test.e. [33:14..33:23): Wildcards -> advanced/Wildcards# @@ -242,7 +242,7 @@ Occurrences: [47:11..47:14): foo -> advanced/Test.foo. [47:15..47:16): A -> local17 [47:19..47:22): foo -> advanced/Test.foo. -[47:22..47:24): .a -> scala/reflect/Selectable#selectDynamic(). +[47:23..47:24): a -> scala/reflect/Selectable#selectDynamic(). [52:6..52:13): HKClass <- advanced/HKClass# [52:13..52:13): <- advanced/HKClass#``(). [52:14..52:15): F <- advanced/HKClass#[F] @@ -1704,8 +1704,8 @@ Occurrences: [5:16..5:19): any <- a/b/Givens.sayHello().(any) [5:21..5:22): A -> a/b/Givens.sayHello().[A] [6:8..6:16): sayHello <- a/b/Givens.sayHello(). +[6:19..6:20): s -> scala/StringContext#s(). [6:34..6:37): any -> a/b/Givens.sayHello().(any) -[6:37..6:38): " -> scala/StringContext#s(). [8:13..8:14): B <- a/b/Givens.sayGoodbye().[B] [8:13..8:14): B <- a/b/Givens.saySoLong().[B] [8:16..8:19): any <- a/b/Givens.sayGoodbye().(any) @@ -1713,11 +1713,11 @@ Occurrences: [8:21..8:22): B -> a/b/Givens.sayGoodbye().[B] [8:21..8:22): B -> a/b/Givens.saySoLong().[B] [9:8..9:18): sayGoodbye <- a/b/Givens.sayGoodbye(). +[9:21..9:22): s -> scala/StringContext#s(). [9:38..9:41): any -> a/b/Givens.sayGoodbye().(any) -[9:41..9:42): " -> scala/StringContext#s(). [10:8..10:17): saySoLong <- a/b/Givens.saySoLong(). +[10:20..10:21): s -> scala/StringContext#s(). [10:37..10:40): any -> a/b/Givens.saySoLong().(any) -[10:40..10:41): " -> scala/StringContext#s(). [12:6..12:12): hello1 <- a/b/Givens.hello1. [12:17..12:25): sayHello -> a/b/Givens.sayHello(). [13:6..13:14): goodbye1 <- a/b/Givens.goodbye1. @@ -1836,12 +1836,12 @@ Occurrences: [20:6..20:7): x <- example/ImplicitConversion#x. [20:9..20:12): Int -> scala/Int# [20:15..20:22): message -> example/ImplicitConversion#message. +[23:2..23:3): s -> scala/StringContext#s(). [23:11..23:18): message -> example/ImplicitConversion#message. [23:20..23:26): number -> example/ImplicitConversion#number. -[23:26..23:27): " -> scala/StringContext#s(). +[24:2..24:3): s -> scala/StringContext#s(). [25:7..25:14): message -> example/ImplicitConversion#message. [26:7..26:13): number -> example/ImplicitConversion#number. -[26:15..26:16): " -> scala/StringContext#s(). [26:17..26:28): stripMargin -> scala/collection/StringOps#stripMargin(+1). [28:6..28:7): a <- example/ImplicitConversion#a. [28:9..28:12): Int -> scala/Int# @@ -3346,6 +3346,73 @@ Occurrences: [13:17..13:17): <- selfs/C6#``(). [13:27..13:28): B -> selfs/B# +expect/StructuralTypes.scala +---------------------------- + +Summary: +Schema => SemanticDB v4 +Uri => StructuralTypes.scala +Text => empty +Language => Scala +Symbols => 12 entries +Occurrences => 33 entries +Synthetics => 4 entries + +Symbols: +example/StructuralTypes. => final object StructuralTypes extends Object { self: StructuralTypes.type => +5 decls } +example/StructuralTypes.User# => type User = Object { abstract method foo (param x: Int): Int; abstract method age => Int; abstract method name => String } +example/StructuralTypes.V. => val method V Object { abstract method scalameta => String } +example/StructuralTypes.fooBar. => val method fooBar Int +example/StructuralTypes.user. => val method user User +local0 => abstract method name => String +local1 => abstract method age => Int +local2 => param x: Int +local3 => abstract method foo (param x: Int): Int +local4 => abstract method scalameta => String +local5 => method scalameta => String +local6 => final class $anon extends Object { self: $anon => +2 decls } + +Occurrences: +[0:8..0:15): example <- example/ +[2:7..2:14): reflect -> scala/reflect/ +[2:15..2:25): Selectable -> scala/reflect/Selectable. +[2:26..2:46): reflectiveSelectable -> scala/reflect/Selectable.reflectiveSelectable(). +[4:7..4:22): StructuralTypes <- example/StructuralTypes. +[5:7..5:11): User <- example/StructuralTypes.User# +[6:8..6:12): name <- local0 +[6:14..6:20): String -> scala/Predef.String# +[7:8..7:11): age <- local1 +[7:13..7:16): Int -> scala/Int# +[8:8..8:11): foo <- local3 +[8:12..8:13): x <- local2 +[8:15..8:18): Int -> scala/Int# +[8:21..8:24): Int -> scala/Int# +[11:6..11:10): user <- example/StructuralTypes.user. +[11:18..11:30): asInstanceOf -> scala/Any#asInstanceOf(). +[11:31..11:35): User -> example/StructuralTypes.User# +[12:2..12:6): user -> example/StructuralTypes.user. +[12:7..12:11): name -> scala/reflect/Selectable#selectDynamic(). +[13:2..13:6): user -> example/StructuralTypes.user. +[13:7..13:10): age -> scala/reflect/Selectable#selectDynamic(). +[14:6..14:12): fooBar <- example/StructuralTypes.fooBar. +[14:15..14:19): user -> example/StructuralTypes.user. +[14:20..14:23): foo -> scala/reflect/Selectable#applyDynamic(). +[16:6..16:7): V <- example/StructuralTypes.V. +[16:9..16:15): Object -> java/lang/Object# +[17:8..17:17): scalameta <- local4 +[17:19..17:25): String -> scala/Predef.String# +[18:6..18:6): <- local6 +[19:8..19:17): scalameta <- local5 +[20:2..20:3): V -> example/StructuralTypes.V. +[20:4..20:13): scalameta -> scala/reflect/Selectable#selectDynamic(). +[21:4..21:19): StructuralTypes -> example/StructuralTypes. + +Synthetics: +[12:2..12:6):user => reflectiveSelectable(*) +[13:2..13:6):user => reflectiveSelectable(*) +[14:15..14:19):user => reflectiveSelectable(*) +[20:2..20:3):V => reflectiveSelectable(*) + expect/Synthetic.scala ---------------------- @@ -3944,7 +4011,7 @@ Occurrences: [48:18..48:19): v -> example/ValUsages.v. [48:20..48:23): yim -> example/Vals#yim(). [49:2..49:3): v -> example/ValUsages.v. -[49:3..49:18): .explicitSetter -> example/Vals#`explicitSetter_=`(). +[49:4..49:18): explicitSetter -> example/Vals#`explicitSetter_=`(). expect/Vararg.scala ------------------- From 0774b2552e03243fff1ab094a77450cbaecede93 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 21 Sep 2023 16:48:13 +0200 Subject: [PATCH 044/134] remove implementation details from enum spec [Cherry-picked a7d1e3332dbee3588fa1da1f2538d0be8d82b7fe] --- docs/_spec/05-classes-and-objects.md | 157 +++++++++++---------------- 1 file changed, 62 insertions(+), 95 deletions(-) diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index d777ebfe73e8..cc8d97704a50 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -904,7 +904,7 @@ Very much like a concrete class definition, an object definition may still conta ```ebnf TmplDef ::= ‘enum’ EnumDef -EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody +EnumDef ::= id ClassConstr [‘extends’ ConstrApps] EnumBody EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’ EnumStat ::= TemplateStat | {Annotation [nl]} {Modifier} EnumCase @@ -925,18 +925,15 @@ First, some terminology and notational conventions: - We use `<...>` for syntactic constructs that in some circumstances might be empty. For instance, `` represents one or more parameter lists `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` or nothing at all. - Enum classes fall into two categories: - - _parameterized_ enum classes have at least one of the following: - - a type parameter section, denoted as `[´\mathit{tps}\,´]`; - - one or more (possibly empty) parameter sections, denoted as `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`. - - _unparameterized_ enum classes have no type parameter sections and no parameter sections. + - _parameterized_ enum classes have at least one or more (possibly empty) term parameter clauses, denoted as `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`. + - _unparameterized_ enum classes have no term parameter clauses, but may optionally have a type parameter clause, denoted as `[´\mathit{tps}\,´]`. - Enum cases fall into three categories: - - - _Class cases_ are those cases that are parameterized, either with a type parameter section `[´\mathit{tps}\,´]` or with one or more (possibly empty) parameter sections `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`. - - _Simple cases_ are cases of an unparameterized enum that have neither parameters nor an extends clause or body. + - _Class enum cases_ are those cases that possibly have a type parameter clause `[´\mathit{tps}\,´]`, and necessarily have one or more (possibly empty) parameter clauses `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`. + - _Simple enum cases_ are those cases that have no parameter clauses and no extends clause. That is, they consist of a name only. - - _Value cases_ are all cases that do not have a parameter section but that do have a (possibly generated) `extends` clause and/or a body. + - _Value enum cases_ are those cases that have no parameter clauses but that do have a (possibly generated) `extends` clause. -- Simple cases and value cases are collectively called _singleton cases_. +- Simple enum cases and value enum cases are collectively called _singleton enum cases_. ###### Example @@ -970,13 +967,11 @@ enum Option[+T]: ### Lowering of Enum Definitions ###### Summary -An enum class is represented as a `sealed` class that extends the `scala.reflect.Enum` trait. +An enum class is represented as a `sealed abstract` class that extends the `scala.reflect.Enum` trait. Enum cases are represented as follows: -- a class case is mapped to a `case class`, -- a singleton case is mapped to a `val` definition, where - - Simple cases all share a single implementation class. - - Value cases will each be implemented by a unique class. +- a class enum case is mapped to a `case class` member of enum class' companion object, +- a singleton enum case is mapped to a `val` member of the enum class' companion object, implemented by a local class definition which may be shared between cases. ###### Precise rules The `scala.reflect.Enum` trait defines a single public method, `ordinal`: @@ -989,106 +984,119 @@ transparent trait Enum extends Any, Product, Serializable: ``` There are nine desugaring rules. Rule (1) desugars enum definitions. -Rules (2) and (3) desugar simple cases. -Rules (4) to (6) define `extends` clauses for cases that are missing them. -Rules (7) to (9) define how such cases with `extends` clauses map into `case class`es or `val`s. +Rule (2) desugars cases of comma-separated names to simple enum cases. +Rules (3) to (7) desugar inferrable details of enum cases. +Rules (8) and (9) define how fully-desugared enum cases map into `case class`es or `val`s. +Explicit `extends` clauses must be provided in the following cases, where rules (2) to (6) do not apply: +- any enum case of a parameterized enum, +- any singleton enum case of an unparameterized enum with non-variant type parameters, +- any class enum case of an enum with type parameters, where the case also has type parameters. 1. An `enum` definition ```scala - enum ´E´ ... { } + enum ´E´ { } ``` expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and an associated companion object that contains the defined cases, expanded according to rules (2 - 8). The enum class starts with a compiler-generated import that imports the names `` of all cases so that they can be used without prefix in the class. ```scala - sealed abstract class ´E´ ... extends with scala.reflect.Enum { - import ´E´.{ } - + sealed abstract class ´E´ + extends with scala.reflect.Enum { + import ´E´.{ } + } object ´E´ { } ``` -2. A singleton case consisting of a comma-separated list of enum names +2. A simple enum case consisting of a comma-separated list of names ```scala case ´C_1´, ..., ´C_n´ ``` - expands to + expands to the following simple enum cases ```scala case ´C_1´; ...; case ´C_n´ ``` Any modifiers or annotations on the original case extend to all expanded cases. - This result is then further rewritten by either (3 or 4). +

This result is then further rewritten by either (3 or 4).

-3. A singleton case without an extends clause +3. A simple enum case `´C´` of an unparameterized enum `´E´` without type parameters ```scala case ´C´ ``` - of an unparameterized enum `´E´` expands to the following simple enum case in `´E´`'s companion object: + expands to the following value enum case: ```scala - val ´C´ = $new(n, "C") + case ´C´ extends ´E´ ``` - Here, `$new` is a private method that creates an instance of ´E´ (see below). + This result is then further rewritten with rule (8). -4. A singleton case without an extends clause +4. A simple enum case `´C´` of an unparameterized enum `´E´[´\mathit{tps}´]` with type parameters ```scala case ´C´ ``` - of an enum `´E´` with type parameters + where `´\mathit{tps}´` are of the following form ```scala ´\mathit{v}_1´ ´T_1´ >: ´L_1´ <: ´U_1´ , ... , ´\mathit{v}_n´ ´T_n´ >: ´L_n´ <: ´U_n´ (n > 0) ``` - where each of the variances `´\mathit{v}_i´` is either `'+'` or `'-'`, expands to the following value enum case: + and where each of the variances `´\mathit{v}_i´` is either `'+'` or `'-'`, expands to the following value enum case: ```scala case ´C´ extends ´E´[´B_1´, ..., ´B_n´] ``` where `´B_i´` is `´L_i´` if `´\mathit{v}_i´ = '+'` and `´U_i´` if `´\mathit{v}_i´ = '-'`. - This result is then further rewritten with rule (8). - **NOTE:** It is not permitted for enums with non-variant type parameters to have singleton cases without an extends clause. +

This result is then further rewritten with rule (8).

-5. A class case without an extends clause +5. A class enum case with type parameters, but without an extends clause ```scala - case ´C´ + case ´C´[´\mathit{tps}´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) ``` - of an enum `´E´` that does not take type parameters expands to + of an unparameterized enum `´E´` without type parameters expands to ```scala - case ´C´ extends ´E´ + case ´C´[´\mathit{tps}´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) extends ´E´ ``` This result is then further rewritten with rule (9). -6. If `´E´` is an enum with type parameters `´\mathit{tps}´`, a class case with neither type parameters nor an extends clause +6. A class enum case without type parameters or an extends clause ```scala - case ´C´ + case ´C´(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) ``` - expands to + of an unparameterized enum `´E´[´\mathit{tps}´]` with type parameters expands to ```scala - case ´C´[´\mathit{tps}´] extends ´E´[´\mathit{tps}´] + case ´C´(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) extends ´E´[´\mathit{tps}´] ``` - This result is then further rewritten with rule (9). - For class cases that have type parameters themselves, an extends clause needs to be given explicitly. - + This result is then further rewritten with rule (7). -7. If `´E´` is an enum with type parameters `´\mathit{tps}´`, a class case without type parameters but with an extends clause +7. A class enum case without type parameters, but has an extends clause ```scala - case ´C´ extends + case ´C´(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) extends ``` - expands to + of an enum `´E´[´\mathit{tps}´]` with type parameters expands to ```scala - case ´C´[´\mathit{tps}´] extends + case ´C´[´\mathit{tps}´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) extends ``` - provided at least one of the parameters `´\mathit{tps}´` is mentioned in a parameter type in `` or in a type argument in ``. + provided at least one of the parameters `´\mathit{tps}´` is mentioned in a parameter type in `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` or in a type argument in ``. +

+ This result is then further rewritten with rule (9). -8. A value case +8. A singleton enum case ```scala case ´C´ extends ``` expands to the following `val` definition in `´E´`'s companion object: ```scala - val ´C´ = new { ; def ordinal = ´\mathit{n}´ } + val ´C´ = $factory(_$ordinal = ´\mathit{n}´, $name = "C") ``` where `´\mathit{n}´` is the ordinal number of the case in the companion object, starting from 0. + `$factory` is a placeholder that expands its arguments into an expression that produces something equivalent to + a new instance of the following (possibly shared) anonymous class: + ```scala + new { + def ordinal: Int = _$ordinal + override def toString: String = $name + } + ``` The anonymous class also implements the abstract `Product` methods that it inherits from `Enum`. +

**NOTE:** It is an error if a value case refers to a type parameter of `´E´` in a type argument within ``. -9. A class case +9. A class enum case ```scala case ´C´ extends ``` @@ -1099,6 +1107,7 @@ Rules (7) to (9) define how such cases with `extends` clauses map into `case cla } ``` where `´\mathit{n}´` is the ordinal number of the case in the companion object, starting from 0. +

**NOTE:** It is an error if a class case refers to a type parameter of `´E´` in a parameter type in `` or `` or in a type argument of ``, unless that parameter is already a type parameter of the case, i.e. the parameter name is defined in ``. ###### Superclass of an enum case @@ -1131,34 +1140,6 @@ private def $new(_$ordinal: Int, $name: String) = override def toString = $name ``` - -###### Example - -Consider the more complex enumeration `Color`, consisting of value enum cases: -```scala -enum Color(val rgb: Int): - case Red extends Color(0xFF0000) - case Green extends Color(0x00FF00) - case Blue extends Color(0x0000FF) -``` - -The three value cases will expand as follows in the companion of `Color`: - -```scala -val Red = new Color(0xFF0000): - def ordinal: Int = 0 - override def productPrefix: String = "Red" - override def toString: String = "Red" -val Green = new Color(0x00FF00): - def ordinal: Int = 1 - override def productPrefix: String = "Green" - override def toString: String = "Green" -val Blue = new Color(0x0000FF): - def ordinal: Int = 2 - override def productPrefix: String = "Blue" - override def toString: String = "Blue" -``` - ### Widening of enum cases post-construction The compiler-generated `apply` and `copy` methods of an class enum case ```scala @@ -1176,20 +1157,6 @@ An enum `´E´` (possibly generic) that defines one or more singleton cases, and It returns the singleton case value whose identifier is `name`. - A method `values` which returns an `Array[´E'´]` of all singleton case values defined by `E`, in the order of their definitions. -### Factory method for simple enum cases - -If an enum `´E´` contains at least one simple case, its companion object will define in addition: - - - A private method `$new` which defines a new simple case value with given ordinal number and name. - This method can be thought as being defined as follows. - - ```scala - private def $new(_$ordinal: Int, $name: String): ´E´ with runtime.EnumValue - ``` - - `$new` returns a new instance of an anonymous class which implements the abstract `Product` methods that it inherits from `Enum`. - - if `´E´` inherits from `java.lang.Enum` the anonymous class does not override the `ordinal` or `toString` methods, as these are final in `java.lang.Enum`. - Additionally `productPrefix` will delegate to `this.name`. - ### Translation of Java-compatible enums A Java-compatible enum is an enum that extends `java.lang.Enum`. From 1c88c152eee5d5cd4c7363148f5c2b0c8d5b0fd1 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 22 Sep 2023 11:56:50 +0200 Subject: [PATCH 045/134] address review comments [Cherry-picked 57a0f174762e6009e46c77ba309d33b5301d499d] --- docs/_spec/05-classes-and-objects.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index cc8d97704a50..e1d4ace3d81f 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -970,8 +970,8 @@ enum Option[+T]: An enum class is represented as a `sealed abstract` class that extends the `scala.reflect.Enum` trait. Enum cases are represented as follows: -- a class enum case is mapped to a `case class` member of enum class' companion object, -- a singleton enum case is mapped to a `val` member of the enum class' companion object, implemented by a local class definition which may be shared between cases. +- a class enum case is mapped to a `case class` member of the enum class' companion object, +- a singleton enum case is mapped to a `val` member of the enum class' companion object, implemented by a local class definition. Whether that local class is shared with other singleton cases, and which ones, is left as an implementation detail. ###### Precise rules The `scala.reflect.Enum` trait defines a single public method, `ordinal`: @@ -994,7 +994,7 @@ Explicit `extends` clauses must be provided in the following cases, where rules 1. An `enum` definition ```scala - enum ´E´ { } + enum ´E´ extends { } ``` expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and an associated companion object that contains the defined cases, expanded according to rules (2 - 8). The enum class starts with a compiler-generated import that imports the names `` of all cases so that they can be used without prefix in the class. From 75ef6ae4e419a87d871d131deda77cbd5a01ad63 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Mon, 25 Sep 2023 13:11:04 +0200 Subject: [PATCH 046/134] fix regression: inline match crash when rhs uses private inlined methods [Cherry-picked a2c0519c00cc933a5477c38b20c55e4b96f773b1] --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 +- .../dotty/tools/dotc/CompilationTests.scala | 1 + tests/pos-special/i18589/core_0.scala | 17 +++++++++++++++++ tests/pos-special/i18589/test_1.scala | 8 ++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/pos-special/i18589/core_0.scala create mode 100644 tests/pos-special/i18589/test_1.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c202f7ccb523..590747738431 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -455,7 +455,7 @@ object Trees { val point = span.point if name.toTermName == nme.ERROR then Span(point) - else if qualifier.span.start > span.point then // right associative + else if qualifier.span.exists && qualifier.span.start > span.point then // right associative val realName = name.stripModuleClassSuffix.lastPart Span(span.start, span.start + realName.length, point) else diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 01c369d40a5d..c998dbec6721 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -38,6 +38,7 @@ class CompilationTests { compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")), compileFile("tests/pos-special/utf8encoded.scala", defaultOptions.and("-encoding", "UTF8")), compileFile("tests/pos-special/utf16encoded.scala", defaultOptions.and("-encoding", "UTF16")), + compileDir("tests/pos-special/i18589", defaultOptions.and("-Ysafe-init").without("-Ycheck:all")), // Run tests for legacy lazy vals compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init", "-Ylegacy-lazy-vals", "-Ycheck-constraint-deps"), FileFilter.include(TestSources.posLazyValsAllowlist)), compileDir("tests/pos-special/java-param-names", defaultOptions.withJavacOnlyOptions("-parameters")), diff --git a/tests/pos-special/i18589/core_0.scala b/tests/pos-special/i18589/core_0.scala new file mode 100644 index 000000000000..d381fd0dea29 --- /dev/null +++ b/tests/pos-special/i18589/core_0.scala @@ -0,0 +1,17 @@ +import scala.deriving.Mirror + +trait NamedCodec[A, R] + +object NamedCodecPlatform { + + final class Builder[R]() { + inline def of[T](using m: Mirror.Of[T]): NamedCodec[T, R] = + inline m match { + case s: Mirror.SumOf[T] => sumInst(s) + case _: Mirror.ProductOf[T] => productInst + } + + private inline def productInst[T]: NamedCodec[T, R] = ??? + private inline def sumInst[T](m: Mirror.SumOf[T]): NamedCodec[T, R] = ??? + } +} diff --git a/tests/pos-special/i18589/test_1.scala b/tests/pos-special/i18589/test_1.scala new file mode 100644 index 000000000000..6de191970791 --- /dev/null +++ b/tests/pos-special/i18589/test_1.scala @@ -0,0 +1,8 @@ +enum Data { + case A, B, C +} + +@main def Test = { + val builder: NamedCodecPlatform.Builder[Any] = ??? + builder.of[Data] +} From 03dd12b8244ec09d5567faa080d08568c4b6bdcd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 26 Sep 2023 11:56:01 +0100 Subject: [PATCH 047/134] Exclude test file that fails on Scala.js + JVM 8 [Cherry-picked 5264c477dd02fd5289c6b8865afd8d536f78f3e0] --- tests/run/StringConcat.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/run/StringConcat.scala b/tests/run/StringConcat.scala index d0a9c32e1016..96d97bf6e664 100644 --- a/tests/run/StringConcat.scala +++ b/tests/run/StringConcat.scala @@ -1,3 +1,9 @@ +// scalajs: --skip + +// Under JVM 8, +// this sometimes blows a StackOverflowError +// in PrepJSInterop + @main def Test() = { // This should generally obey 15.18.1. of the JLS (String Concatenation Operator +) From 6af623cc8e1a5b9dedb9106c83e4a3adb56c9e36 Mon Sep 17 00:00:00 2001 From: Jakub Ciesluk <323892@uwr.edu.pl> Date: Mon, 25 Sep 2023 16:25:17 +0200 Subject: [PATCH 048/134] bugfix: No signature help for local methods [Cherry-picked 0fd88ee3eb394184384cc655afb2547f302ca2f4] --- .../dotty/tools/dotc/util/Signatures.scala | 4 +- .../signaturehelp/SignatureHelpSuite.scala | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 5fae39a20de4..5bb79642278d 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -13,6 +13,7 @@ import core.NameKinds import core.Types._ import core.Symbols.NoSymbol import interactive.Interactive +import transform.SymUtils.isLocalToBlock import util.Spans.Span import reporting._ @@ -178,7 +179,8 @@ object Signatures { (alternativeIndex, alternatives) case _ => val funSymbol = fun.symbol - val alternatives = funSymbol.owner.info.member(funSymbol.name).alternatives + val alternatives = if funSymbol.isLocalToBlock then List(funSymbol.denot) else + funSymbol.owner.info.member(funSymbol.name).alternatives val alternativeIndex = alternatives.map(_.symbol).indexOf(funSymbol) max 0 (alternativeIndex, alternatives) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala index 0b3112228b83..6d8e6abca0a1 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala @@ -702,3 +702,58 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | ^^^^^^^^^^^ |""".stripMargin ) + + @Test def `local-method` = + check( + """ + |object Main { + | def foo() = { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(@@) + | } + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^^^^ + |""".stripMargin, + ) + + @Test def `local-method2` = + check( + """ + |object Main { + | val foo = { + | def deployment( + | fst: String, + | snd: Int = 1, + | ): Option[Int] = ??? + | deployment(@@) + | } + |} + |""".stripMargin, + """|deployment(fst: String, snd: Int): Option[Int] + | ^^^^^^^^^^^ + |""".stripMargin, + ) + + @Test def `local-method3` = + check( + """ + |object Main { + | def foo = { + | object a { + | def apply(a: Int): Int = a + | def apply(b: String): String = b + | a(""@@) + | } + | } + |} + |""".stripMargin, + """|apply(b: String): String + | ^^^^^^^^^ + |apply(a: Int): Int + |""".stripMargin + ) \ No newline at end of file From 896042866ba4ce59532bada1ea19f17c94f7e3b3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 26 Sep 2023 09:41:47 +0200 Subject: [PATCH 049/134] Fix typos in ExtensionMethods [Cherry-picked 56e1bac04f9e9395122be3255ae11b9ef09d05ca] --- .../src/dotty/tools/dotc/transform/ExtensionMethods.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala index a430f7532066..19124357a0bd 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -215,8 +215,8 @@ object ExtensionMethods { | | ${candidates.map(c => s"${c.name}:${c.info.signature}:${FullParameterization.memberSignature(c.info)}").mkString("\n")}""") if matching.tail.nonEmpty then - // this case will report a "have the same erasure" error later at erasure pahse - report.log(i"mutiple extension methods match $imeth: ${candidates.map(c => i"${c.name}:${c.info}")}") + // this case will report a "have the same erasure" error later at erasure phase + report.log(i"multiple extension methods match $imeth: ${candidates.map(c => i"${c.name}:${c.info}")}") matching.head.symbol.asTerm } } From 20817c5599c277ee94bf632108bbc51bf7522dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 28 Sep 2023 11:18:10 +0200 Subject: [PATCH 050/134] [spec] Remove highlighting in the lists of keywords. The highlighter does not print all keywords with the same color, which makes sense in actual code snippets, but is confusing in the raw lists of all keywords. [Cherry-picked bd19d15a85b8e4cfb466513a04861f08ac9f9932] --- docs/_spec/01-lexical-syntax.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index a2b5c4c423a9..9ced89228e22 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -83,7 +83,7 @@ For this purpose, lower case letters include not only a-z, but also all characte The following are examples of variable identifiers: -> ```scala +> ``` > x maxIndex p2p empty_? > `yield` αρετη _y dot_product_* > __system _MAX_LEN_ @@ -92,7 +92,7 @@ The following are examples of variable identifiers: Some examples of constant identifiers are -> ```scala +> ``` > + Object $reserved Džul ǂnûm > ⅰ_ⅲ Ⅰ_Ⅲ ↁelerious ǃqhàà ʹthatsaletter > ``` @@ -104,7 +104,7 @@ User programs should not define identifiers that contain ‘$’ characters. The following names are reserved words instead of being members of the syntactic class `id` of lexical identifiers. -```scala +``` abstract case catch class def do else enum export extends false final finally for given if implicit import lazy match new @@ -169,14 +169,14 @@ A newline in a Scala source text is treated as the special token “nl” if the The tokens that can terminate a statement are: literals, identifiers and the following delimiters and reserved words: -```scala +``` this null true false return type _ ) ] } ``` The tokens that can begin a statement are all Scala tokens _except_ the following delimiters and reserved words: -```scala +``` catch else extends finally forSome match with yield , . ; : = => <- <: <% >: # [ ) ] } @@ -452,7 +452,7 @@ Characters must not necessarily be printable; newlines or other control characte > > This would produce the string: > -> ```scala +> ``` > the present string > spans three > lines. @@ -469,7 +469,7 @@ Characters must not necessarily be printable; newlines or other control characte > > evaluates to > -> ```scala +> ``` > the present string > spans three > lines. From 681a549f6575bfe37e7dec70363981ca8c5a6572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 28 Sep 2023 11:23:09 +0200 Subject: [PATCH 051/134] [spec] Fix the GitHub URL to edit the spec files. [Cherry-picked b4ce68461faa6cc4283d0b557c23665505b5babf] --- docs/_spec/_layouts/default.yml | 2 +- docs/_spec/_layouts/toc.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_spec/_layouts/default.yml b/docs/_spec/_layouts/default.yml index 2589a105dff2..5d597cb5ea96 100644 --- a/docs/_spec/_layouts/default.yml +++ b/docs/_spec/_layouts/default.yml @@ -27,7 +27,7 @@
From a3a06d992030a4d069dd2e431d6c585215fa978c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 28 Sep 2023 11:45:20 +0200 Subject: [PATCH 052/134] [spec] Better align EBNF grammars in the lexical syntax chapter. [Cherry-picked 18c8f91d606cddae9642aac233a92ed4cf0ae1ba] --- docs/_spec/01-lexical-syntax.md | 39 +++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index 9ced89228e22..9f7c3ba76a24 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -43,17 +43,17 @@ colon ::= ':' -- with side conditions explained above ## Identifiers ```ebnf -op ::= opchar {opchar} -varid ::= lower idrest -boundvarid ::= varid - | ‘`’ varid ‘`’ -alphaid ::= upper idrest - | varid -plainid ::= alphaid - | op -id ::= plainid - | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ -idrest ::= {letter | digit} [‘_’ op] +op ::= opchar {opchar} +varid ::= lower idrest +boundvarid ::= varid + | ‘`’ varid ‘`’ +alphaid ::= upper idrest + | varid +plainid ::= alphaid + | op +id ::= plainid + | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ +idrest ::= {letter | digit} [‘_’ op] escapeSeq ::= UnicodeEscape | charEscapeSeq UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ @@ -177,9 +177,8 @@ _ ) ] } The tokens that can begin a statement are all Scala tokens _except_ the following delimiters and reserved words: ``` -catch else extends finally forSome match -with yield , . ; : = => <- <: <% ->: # [ ) ] } +catch else extends finally forSome match with yield +, . ; : = => <- <: <% >: # [ ) ] } ``` A `case` token can begin a statement only if followed by a @@ -334,8 +333,7 @@ Literal ::= [‘-’] integerLiteral ### Integer Literals ```ebnf -integerLiteral ::= (decimalNumeral | hexNumeral) - [‘L’ | ‘l’] +integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] decimalNumeral ::= ‘0’ | digit [{digit | ‘_’} digit] hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] ``` @@ -366,11 +364,10 @@ The digits of a numeric literal may be separated by arbitrarily many underscores ### Floating Point Literals ```ebnf -floatingPointLiteral - ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] - | decimalNumeral exponentPart [floatType] - | decimalNumeral floatType -exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] +floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] + | decimalNumeral exponentPart [floatType] + | decimalNumeral floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] ``` Floating point literals are of type `Float` when followed by a floating point type suffix `F` or `f`, and are of type `Double` otherwise. From 71751abae96e0be5cf7a563dbd5c4e3a0afaaffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 28 Sep 2023 16:24:28 +0200 Subject: [PATCH 053/134] Spec: Update the list of tokens that can start/end statements. [Cherry-picked 500ef4d4972c1c561c50d97a1767c266696232f1] --- docs/_spec/01-lexical-syntax.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index 9f7c3ba76a24..7dfcea87bd2d 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -170,20 +170,19 @@ A newline in a Scala source text is treated as the special token “nl” if the The tokens that can terminate a statement are: literals, identifiers and the following delimiters and reserved words: ``` -this null true false return type -_ ) ] } +this null true false return type given +_ ) ] } outdent ``` The tokens that can begin a statement are all Scala tokens _except_ the following delimiters and reserved words: ``` -catch else extends finally forSome match with yield -, . ; : = => <- <: <% >: # [ ) ] } +catch do else extends finally forSome macro +match then with yield +, . ; : = => <- <: <% >: # =>> ?=> +) ] } outdent ``` -A `case` token can begin a statement only if followed by a -`class` or `object` token. - A _leading infix operator_ is a symbolic identifier such as `+`, or `approx_==`, or an identifier in backticks that: - starts a new line, and From fc44d9ca61dfde2f391bbb31d234069fd2433f01 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:09:54 +0200 Subject: [PATCH 054/134] use directives instead of scalac options in tests [Cherry-picked c16fb4e13c9fbddce0d7541f7c566955a6f65023][modified] --- compiler/test-resources/repl/i13208.scala | 2 +- compiler/test-resources/repl/rewrite-messages | 2 +- compiler/test/dotty/tools/repl/ReplTest.scala | 2 +- compiler/test/dotty/tools/utils.scala | 31 +++++++++++-------- .../macro/pos/t8013/inpervolated_2.scala | 4 +-- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/compiler/test-resources/repl/i13208.scala b/compiler/test-resources/repl/i13208.scala index ce4fcf0d9ed8..61ace43c732d 100644 --- a/compiler/test-resources/repl/i13208.scala +++ b/compiler/test-resources/repl/i13208.scala @@ -1,4 +1,4 @@ -// scalac: -source:future -deprecation +//> using options -source:future -deprecation scala> type M[X] = X match { case Int => String case _ => Int } scala> type N[X] = X match { case List[_] => Int } 1 warning found diff --git a/compiler/test-resources/repl/rewrite-messages b/compiler/test-resources/repl/rewrite-messages index eee2fe034c43..a63a72195019 100644 --- a/compiler/test-resources/repl/rewrite-messages +++ b/compiler/test-resources/repl/rewrite-messages @@ -1,4 +1,4 @@ -// scalac: -source:future-migration -deprecation -Werror +//> using options -source:future-migration -deprecation -Werror scala> import scala.util._ -- Error: ---------------------------------------------------------------------- 1 | import scala.util._ diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index 34cad747fde6..8fbf635c9a17 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -69,7 +69,7 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na val expectedOutput = lines.filter(nonBlank) val actualOutput = { - val opts = toolArgsFor(ToolName.Scalac)(lines.take(1)) + val opts = toolArgsFor(ToolName.Scalac, scriptFile.map(_.toString))(lines.take(1)) val (optsLine, inputLines) = if opts.isEmpty then ("", lines) else (lines.head, lines.drop(1)) resetToInitial(opts) diff --git a/compiler/test/dotty/tools/utils.scala b/compiler/test/dotty/tools/utils.scala index 8c154a38850d..8161631acb44 100644 --- a/compiler/test/dotty/tools/utils.scala +++ b/compiler/test/dotty/tools/utils.scala @@ -65,7 +65,7 @@ type ToolArgs = Map[ToolName, List[String]] */ def toolArgsFor(files: List[JPath], charset: Charset = UTF_8): ToolArgs = files.foldLeft(Map.empty[ToolName, List[String]]) { (res, path) => - val toolargs = toolArgsParse(resource(Files.lines(path, charset))(_.limit(10).toScala(List))) + val toolargs = toolArgsParse(resource(Files.lines(path, charset))(_.limit(10).toScala(List)), Some(path.toString)) toolargs.foldLeft(res) { case (acc, (tool, args)) => val name = ToolName.named(tool) @@ -74,31 +74,36 @@ def toolArgsFor(files: List[JPath], charset: Charset = UTF_8): ToolArgs = } } -def toolArgsFor(tool: ToolName)(lines: List[String]): List[String] = - toolArgsParse(lines).collectFirst { case (name, args) if tool eq ToolName.named(name) => CommandLineParser.tokenize(args) }.getOrElse(Nil) +def toolArgsFor(tool: ToolName, filename: Option[String])(lines: List[String]): List[String] = + toolArgsParse(lines, filename).collectFirst { case (name, args) if tool eq ToolName.named(name) => CommandLineParser.tokenize(args) }.getOrElse(Nil) -// scalac: arg1 arg2, with alternative opening, optional space, alt names, text that is not */ up to end. +// scalajs: arg1 arg2, with alternative opening, optional space, alt names, text that is not */ up to end. // groups are (name, args) +// note: ideally we would replace everything that requires this to use directive syntax, however scalajs: --skip has no directive equivalent yet. private val toolArg = raw"(?://|/\*| \*) ?(?i:(${ToolName.values.mkString("|")})):((?:[^*]|\*(?!/))*)".r.unanchored private val directiveOptionsArg = raw"//> using options (.*)".r.unanchored // Inspect the lines for compiler options of the form -// `// scalac: args`, `/* scalac: args`, ` * scalac: args`. +// `//> using options args`, `// scalajs: args`, `/* scalajs: args`, ` * scalajs: args` etc. // If args string ends in close comment, stop at the `*` `/`. // Returns all the matches by the regex. -def toolArgsParse(lines: List[String]): List[(String,String)] = - lines.flatMap { case toolArg(name, args) => List((name, args)) case _ => Nil } ++ +def toolArgsParse(lines: List[String], filename: Option[String]): List[(String,String)] = + lines.flatMap { + case toolArg("scalac", _) => sys.error(s"`// scalac: args` not supported. Please use `//> using options args`${filename.fold("")(f => s" in file $f")}") + case toolArg(name, args) => List((name, args)) + case _ => Nil + } ++ lines.flatMap { case directiveOptionsArg(args) => List(("scalac", args)) case _ => Nil } import org.junit.Test import org.junit.Assert._ class ToolArgsTest: - @Test def `missing toolarg is absent`: Unit = assertEquals(Nil, toolArgsParse(List(""))) - @Test def `toolarg is present`: Unit = assertEquals(("test", " -hey") :: Nil, toolArgsParse("// test: -hey" :: Nil)) - @Test def `tool is present`: Unit = assertEquals("-hey" :: Nil, toolArgsFor(ToolName.Test)("// test: -hey" :: Nil)) - @Test def `missing tool is absent`: Unit = assertEquals(Nil, toolArgsFor(ToolName.Javac)("// test: -hey" :: Nil)) + @Test def `missing toolarg is absent`: Unit = assertEquals(Nil, toolArgsParse(List(""), None)) + @Test def `toolarg is present`: Unit = assertEquals(("test", " -hey") :: Nil, toolArgsParse("// test: -hey" :: Nil, None)) + @Test def `tool is present`: Unit = assertEquals("-hey" :: Nil, toolArgsFor(ToolName.Test, None)("// test: -hey" :: Nil)) + @Test def `missing tool is absent`: Unit = assertEquals(Nil, toolArgsFor(ToolName.Javac, None)("// test: -hey" :: Nil)) @Test def `multitool is present`: Unit = - assertEquals("-hey" :: Nil, toolArgsFor(ToolName.Test)("// test: -hey" :: "// javac: -d /tmp" :: Nil)) - assertEquals("-d" :: "/tmp" :: Nil, toolArgsFor(ToolName.Javac)("// test: -hey" :: "// javac: -d /tmp" :: Nil)) + assertEquals("-hey" :: Nil, toolArgsFor(ToolName.Test, None)("// test: -hey" :: "// javac: -d /tmp" :: Nil)) + assertEquals("-d" :: "/tmp" :: Nil, toolArgsFor(ToolName.Javac, None)("// test: -hey" :: "// javac: -d /tmp" :: Nil)) end ToolArgsTest diff --git a/tests/disabled/macro/pos/t8013/inpervolated_2.scala b/tests/disabled/macro/pos/t8013/inpervolated_2.scala index 90e571b42c8c..cbe5139cef5a 100644 --- a/tests/disabled/macro/pos/t8013/inpervolated_2.scala +++ b/tests/disabled/macro/pos/t8013/inpervolated_2.scala @@ -1,6 +1,4 @@ -/* - * scalac: -Xfatal-warnings -Xlint - */ +//> using options -Xfatal-warnings -Xlint package t8013 // unsuspecting user of perverse macro From 057d8f3a0cbaf277905bb0e2a85a660178106f70 Mon Sep 17 00:00:00 2001 From: Carl Date: Mon, 8 May 2023 21:20:02 +0200 Subject: [PATCH 055/134] Implement -Xlint:private-shadow, type-parameter-shadow Respectively warn about : - a private field or a class parameter that shadows a superclass field - a local type parameter that shadows a type already in the scope Fixes : #17612 and #17613 [Cherry-picked e5fd4773f2cb24a40ad946f12fb09da579139ad0] --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../tools/dotc/config/ScalaSettings.scala | 26 +- .../tools/dotc/transform/CheckShadowing.scala | 314 ++++++++++++++++++ .../fatal-warnings/i17612a.check | 32 ++ .../fatal-warnings/i17612a.scala | 42 +++ .../fatal-warnings/i17613a.check | 28 ++ .../fatal-warnings/i17613a.scala | 23 ++ .../fatal-warnings/i17613b.check | 44 +++ .../fatal-warnings/i17613b/i17613b.scala | 33 ++ .../fatal-warnings/i17613b/importTry.scala | 5 + 10 files changed, 547 insertions(+), 2 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17612a.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17612a.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17613a.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17613a.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17613b.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6118732d4ae..743aca5bf90a 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckUnused.PostTyper, new CheckShadowing) :: // Check for unused and shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 312329c0f85d..162c765926dd 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.config.SourceVersion import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.rewrites.Rewrites import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory} +import Setting.ChoiceWithHelp import scala.util.chaining._ @@ -156,7 +157,6 @@ private sealed trait VerboseSettings: */ private sealed trait WarningSettings: self: SettingGroup => - import Setting.ChoiceWithHelp val Whelp: Setting[Boolean] = BooleanSetting("-W", "Print a synopsis of warning options.") val XfatalWarnings: Setting[Boolean] = BooleanSetting("-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) @@ -307,6 +307,30 @@ private sealed trait XSettings: } val XmacroSettings: Setting[List[String]] = MultiStringSetting("-Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") + + val Xlint: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + name = "-Xlint", + helpArg = "advanced warning", + descr = "Enable or disable specific `lint` warnings", + choices = List( + ChoiceWithHelp("nowarn", ""), + ChoiceWithHelp("all", ""), + ChoiceWithHelp("private-shadow", "Warn if a private field or class parameter shadows a superclass field"), + ChoiceWithHelp("type-parameter-shadow", "Warn when a type parameter shadows a type already in the scope"), + ), + default = Nil + ) + + object XlintHas: + def isChoiceSet(s: String)(using Context) = Xlint.value.pipe(us => us.contains(s)) + def allOr(s: String)(using Context) = Xlint.value.pipe(us => us.contains("all") || us.contains(s)) + def nowarn(using Context) = allOr("nowarn") + + def privateShadow(using Context) = + allOr("private-shadow") + def typeParameterShadow(using Context) = + allOr("type-parameter-shadow") + end XSettings /** -Y "Forking" as in forked tongue or "Private" settings */ diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala new file mode 100644 index 000000000000..1c575fdc89a1 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -0,0 +1,314 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees.EmptyTree +import dotty.tools.dotc.transform.MegaPhase +import dotty.tools.dotc.transform.MegaPhase.MiniPhase +import dotty.tools.dotc.report +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Flags.* +import dotty.tools.dotc.util.{Property, SrcPos} +import dotty.tools.dotc.core.Symbols.ClassSymbol +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Flags.EmptyFlags +import dotty.tools.dotc.ast.tpd.TreeTraverser +import dotty.tools.dotc.core.Types.watchList +import dotty.tools.dotc.core.Types.NoType +import dotty.tools.dotc.core.Types.Type +import dotty.tools.dotc.core.Types +import dotty.tools.dotc.semanticdb.TypeOps +import dotty.tools.dotc.cc.boxedCaptureSet +import dotty.tools.dotc.core.Symbols.NoSymbol +import dotty.tools.dotc.transform.SymUtils.isParamOrAccessor +import scala.collection.mutable +import dotty.tools.dotc.core.Scopes.Scope +import scala.collection.immutable.HashMap +import dotty.tools.dotc.core.Symbols +import dotty.tools.dotc.typer.ImportInfo +import dotty.tools.dotc.ast.untpd.ImportSelector +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.ast.Trees.Ident +import dotty.tools.dotc.core.Names.TypeName +import dotty.tools.dotc.core.Names.TermName +import dotty.tools.dotc.core.Mode.Type +import dotty.tools.dotc.core.Names.SimpleName + +class CheckShadowing extends MiniPhase: + import CheckShadowing.* + import ShadowingData.* + + private val _key = Property.Key[ShadowingData] + + private def shadowingDataApply[U](f: ShadowingData => U)(using Context): Context = + ctx.property(_key).foreach(f) + ctx + + override def phaseName: String = CheckShadowing.name + + override def description: String = CheckShadowing.description + + override def isRunnable(using Context): Boolean = + super.isRunnable && + ctx.settings.Xlint.value.nonEmpty && + !ctx.isJava + + // Setup before the traversal + override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + val data = ShadowingData() + val fresh = ctx.fresh.setProperty(_key, data) + shadowingDataApply(sd => sd.registerRootImports())(using fresh) + + // Reporting on traversal's end + override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = + shadowingDataApply(sd => + reportShadowing(sd.getShadowingResult) + ) + tree + + // MiniPhase traversal : + + override def prepareForPackageDef(tree: tpd.PackageDef)(using Context): Context = + shadowingDataApply(sd => sd.inNewScope()) + ctx + + override def prepareForTemplate(tree: tpd.Template)(using Context): Context = + shadowingDataApply(sd => sd.inNewScope()) + ctx + + override def prepareForBlock(tree: tpd.Block)(using Context): Context = + shadowingDataApply(sd => sd.inNewScope()) + ctx + + override def prepareForOther(tree: tpd.Tree)(using Context): Context = + importTraverser(tree.symbol).traverse(tree) + ctx + + override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = + shadowingDataApply(sd => + sd.registerPrivateShadows(tree) + ) + + override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = + if tree.symbol.isAliasType then // if alias, the parent is the current symbol + nestedTypeTraverser(tree.symbol).traverse(tree.rhs) + if tree.symbol.is(Param) then // if param, the parent is up + val owner = tree.symbol.owner + val parent = if (owner.isConstructor) then owner.owner else owner + nestedTypeTraverser(parent).traverse(tree.rhs)(using ctx.outer) + shadowingDataApply(sd => sd.registerCandidate(parent, tree)) + else + ctx + + + override def transformPackageDef(tree: tpd.PackageDef)(using Context): tpd.Tree = + shadowingDataApply(sd => sd.outOfScope()) + tree + + override def transformBlock(tree: tpd.Block)(using Context): tpd.Tree = + shadowingDataApply(sd => sd.outOfScope()) + tree + + override def transformTemplate(tree: tpd.Template)(using Context): tpd.Tree = + shadowingDataApply(sd => sd.outOfScope()) + tree + + override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = + if tree.symbol.is(Param) && !tree.symbol.owner.isConstructor then // Do not register for constructors the work is done for the Class owned equivalent TypeDef + shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol.owner)(using ctx.outer)) + if tree.symbol.isAliasType then // No need to start outer here, because the TypeDef reached here it's already the parent + shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol)(using ctx)) + tree + + // Helpers : + + private def reportShadowing(res: ShadowingData.ShadowResult)(using Context): Unit = + res.warnings.sortBy(w => (w.pos.line, w.pos.startPos.column))(using Ordering[(Int, Int)]).foreach { s => + s match + case PrivateShadowWarning(pos, shadow, shadowed) => + report.warning(s"${shadow.showLocated} shadows field ${shadowed.name} inherited from ${shadowed.owner}", pos) + case TypeParamShadowWarning(pos, shadow, parent, shadowed) => + if shadowed.exists then + report.warning(s"Type parameter ${shadow.name} for $parent shadows the type defined by ${shadowed.showLocated}", pos) + else + report.warning(s"Type parameter ${shadow.name} for $parent shadows an explicitly renamed type : ${shadow.name}", pos) + } + + private def nestedTypeTraverser(parent: Symbol) = new TreeTraverser: + import tpd._ + + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match + case t:tpd.TypeDef => + val newCtx = shadowingDataApply(sd => + sd.registerCandidate(parent, t) + ) + traverseChildren(tree)(using newCtx) + case _ => + traverseChildren(tree) + end traverse + end nestedTypeTraverser + + // To reach the imports during a miniphase traversal + private def importTraverser(parent: Symbol) = new TreeTraverser: + import tpd._ + + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match + case t:tpd.Import => + shadowingDataApply(sd => sd.registerImport(t)) + traverseChildren(tree) + case _ => + traverseChildren(tree) + +end CheckShadowing + + +object CheckShadowing: + + val name = "checkShadowing" + val description = "check for elements shadowing other elements in scope" + + private class ShadowingData: + import dotty.tools.dotc.transform.CheckShadowing.ShadowingData._ + import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} + + private val rootImports = MutSet[SingleDenotation]() + private val explicitsImports = MutStack[MutSet[tpd.Import]]() + private val renamedImports = MutStack[MutMap[SimpleName, Name]]() // original name -> renamed name + + private val typeParamCandidates = MutMap[Symbol, Seq[tpd.TypeDef]]().withDefaultValue(Seq()) + private val shadowedTypeDefs = MutSet[TypeParamShadowWarning]() + + private val shadowedPrivateDefs = MutSet[PrivateShadowWarning]() + + def inNewScope()(using Context) = + explicitsImports.push(MutSet()) + renamedImports.push(MutMap()) + + def outOfScope()(using Context) = + explicitsImports.pop() + renamedImports.pop() + + /** Register the Root imports (at once per compilation unit)*/ + def registerRootImports()(using Context) = + ctx.definitions.rootImportTypes.foreach(rimp => println()) + val langPackageName = ctx.definitions.JavaLangPackageVal.name.toSimpleName // excludes lang package + rootImports.addAll(ctx.definitions.rootImportTypes.withFilter(_.name.toSimpleName != langPackageName).flatMap(_.typeMembers)) + + /* Register an import encountered in the current scope **/ + def registerImport(imp: tpd.Import)(using Context) = + val renamedImps = imp.selectors.collect(sel => { sel.renamed match + case Ident(rename) => + (sel.name.toSimpleName, rename) + }).toMap + explicitsImports.top += imp + renamedImports.top.addAll(renamedImps) + + /** Register a potential type definition which could shadows a Type already defined */ + def registerCandidate(parent: Symbol, typeDef: tpd.TypeDef) = + val actual = typeParamCandidates.getOrElseUpdate(parent, Seq()) + typeParamCandidates.update(parent, actual.+:(typeDef)) + + /** Compute if there is some TypeParam shadowing and register if it is the case*/ + def computeTypeParamShadowsFor(parent: Symbol)(using Context): Unit = + typeParamCandidates(parent).foreach(typeDef => { + val sym = typeDef.symbol + val shadowedType = + lookForRootShadowedType(sym) + .orElse(lookForImportedShadowedType(sym)) + .orElse(lookForUnitShadowedType(sym)) + shadowedType.foreach(shadowed => + if !renamedImports.exists(_.contains(shadowed.name.toSimpleName)) then + shadowedTypeDefs += TypeParamShadowWarning(typeDef.srcPos, typeDef.symbol, parent, shadowed) + ) + }) + + private def lookForRootShadowedType(symbol: Symbol)(using Context): Option[Symbol] = + rootImports.find(p => p.name.toSimpleName == symbol.name.toSimpleName).map(_.symbol) + + private def lookForImportedShadowedType(symbol: Symbol)(using Context): Option[Symbol] = + explicitsImports + .flatMap(_.flatMap(imp => symbol.isInImport(imp))) + .headOption + + private def lookForUnitShadowedType(symbol: Symbol)(using Context): Option[Symbol] = + if !ctx.owner.exists then + None + else + val declarationScope = ctx.effectiveScope + val res = declarationScope.lookup(symbol.name) + res match + case s: Symbol if s.isType => Some(s) + case _ => lookForUnitShadowedType(symbol)(using ctx.outer) + + /** Register if the valDef is a private declaration that shadows an inherited field */ + def registerPrivateShadows(valDef: tpd.ValDef)(using Context): Unit = + lookForShadowedField(valDef.symbol).foreach(shadowedField => + shadowedPrivateDefs += PrivateShadowWarning(valDef.startPos, valDef.symbol, shadowedField) + ) + + private def lookForShadowedField(symDecl: Symbol)(using Context): Option[Symbol] = + if symDecl.isPrivate then + val symDeclType = symDecl.info + val bClasses = symDecl.owner.info.baseClasses + bClasses match + case _ :: inherited => + inherited + .map(classSymbol => symDecl.denot.matchingDecl(classSymbol, symDeclType)) + .find(sym => sym.name == symDecl.name) + case Nil => + None + else + None + + /** Get the shadowing analysis's result */ + def getShadowingResult(using Context): ShadowResult = + + val privateShadowWarnings: List[ShadowWarning] = + if ctx.settings.XlintHas.privateShadow then + shadowedPrivateDefs.toList + else + Nil + val typeParamShadowWarnings: List[ShadowWarning] = + if ctx.settings.XlintHas.typeParameterShadow then + shadowedTypeDefs.toList + else + Nil + ShadowResult(privateShadowWarnings ++ typeParamShadowWarnings) + + extension (sym: Symbol) + /** Given an import and accessibility, return the import's symbol that matches import<->this symbol */ + private def isInImport(imp: tpd.Import)(using Context): Option[Symbol] = + val tpd.Import(qual, sels) = imp + val simpleSelections = qual.tpe.member(sym.name).alternatives + val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) + + sels.find(is => is.rename.toSimpleName == sym.name.toSimpleName).map(_.symbol) + .orElse(typeSelections.map(_.symbol).find(sd => sd.name == sym.name)) + .orElse(simpleSelections.map(_.symbol).find(sd => sd.name == sym.name)) + + end ShadowingData + + private object ShadowingData: + sealed abstract class ShadowWarning(val pos: SrcPos, val shadow: Symbol, val shadowed: Symbol) + + case class PrivateShadowWarning( + override val pos: SrcPos, + override val shadow: Symbol, + override val shadowed: Symbol + ) extends ShadowWarning(pos, shadow, shadowed) + + case class TypeParamShadowWarning( + override val pos: SrcPos, + override val shadow: Symbol, + val shadowParent: Symbol, + override val shadowed: Symbol, + ) extends ShadowWarning(pos, shadow, shadowed) + + /** A container for the results of the shadow elements analysis */ + case class ShadowResult(warnings: List[ShadowWarning]) + +end CheckShadowing \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i17612a.check b/tests/neg-custom-args/fatal-warnings/i17612a.check new file mode 100644 index 000000000000..fad897b7c5f8 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612a.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:15 ----------------------------------------------------- +18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value x in class Derived shadows field x inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:24 ----------------------------------------------------- +18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value y in class Derived shadows field y inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:20:2 ------------------------------------------------------ +20 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + | ^ + | value shadowed2 in class Derived shadows field shadowed2 inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:21:2 ------------------------------------------------------ +21 | private[this] val shadowed3 = 3 + 3 // error + | ^ + | value shadowed3 in class Derived shadows field shadowed3 inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:23:2 ------------------------------------------------------ +23 | private val shadowed5 = 5 + 5 // error + | ^ + | value shadowed5 in class Derived shadows field shadowed5 inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:20 ----------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value x in class UnderDerived shadows field x inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:28 ----------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value y in class UnderDerived shadows field y inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:36 ----------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value z in class UnderDerived shadows field z inherited from class Base diff --git a/tests/neg-custom-args/fatal-warnings/i17612a.scala b/tests/neg-custom-args/fatal-warnings/i17612a.scala new file mode 100644 index 000000000000..0fb6306b96cb --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612a.scala @@ -0,0 +1,42 @@ +// scalac: -Xlint:private-shadow + +object i17612a: + class Base(var x: Int, val y: Int, var z: Int): + var shadowed2 = 2 + val shadowed3 = 3 + val shadowed4 = 4 + protected var shadowed5 = 5 + //var shadowed6 = 6 + + val notShadowed = -1 + private val notShadowed2 = -2 + //val fatalOverride = 0 + + def increment(): Unit = + x = x + 1 + + class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + private def hello() = 4 + private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + private[this] val shadowed3 = 3 + 3 // error + //private[Derived] val fatalOverride = 0 // value fatalOverride of type Int has weaker access privileges; it should be public + private val shadowed5 = 5 + 5 // error + private val notShadowed2 = -4 + //protected var shadowed6 = 6 + 6 // variable shadowed6 of type Int has weaker access privileges; it should be public + + def inFunctionScope() = + val notShadowed = -2 // OK + -2 + + override def toString = + s"x : ${x.toString}, y : ${y.toString}" + + class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + + def main(args: Array[String]) = + val derived = new Derived(1, 1, 1) + println(derived.toString) // yields x: '1', as expected + derived.increment() + println(derived.toString) // still x: '1', probably unexpected, for y it never prints the super value, less surprising + println(derived.shadowed2) + println(derived.shadowed3) \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i17613a.check b/tests/neg-custom-args/fatal-warnings/i17613a.check new file mode 100644 index 000000000000..b0aeb85101a1 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613a.check @@ -0,0 +1,28 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:8:13 ------------------------------------------------------ +8 | def foobar[D](in: D) = in.toString // error method parameter shadows some other type + | ^ + | Type parameter D for method foobar shadows the type defined by trait D in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:9:13 ------------------------------------------------------ +9 | type MySeq[D] = Seq[D] // error type member's parameter shadows some other type + | ^ + | Type parameter D for type MySeq shadows the type defined by trait D in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:11:12 ----------------------------------------------------- +11 | class Foo[T](t: T): // error class parameter shadows some other type + | ^ + | Type parameter T for class Foo shadows the type defined by type T in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:12:11 ----------------------------------------------------- +12 | def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter + | ^ + | Type parameter T for method bar shadows the type defined by type T in class Foo +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:15:12 ----------------------------------------------------- +15 | class C[M[List[_]]] // error + | ^^^^^^^ + | Type parameter List for class C shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:16:11 ----------------------------------------------------- +16 | type E[M[List[_]]] = Int // error + | ^^^^^^^ + | Type parameter List for type E shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:17:14 ----------------------------------------------------- +17 | def foo[N[M[List[_]]]] = ??? // error + | ^^^^^^^ + | Type parameter List for method foo shadows the type defined by type List in package scala diff --git a/tests/neg-custom-args/fatal-warnings/i17613a.scala b/tests/neg-custom-args/fatal-warnings/i17613a.scala new file mode 100644 index 000000000000..4639bd4b5053 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613a.scala @@ -0,0 +1,23 @@ +// scalac: -Xlint:type-parameter-shadow + +object i17613a: + class B: + type T = Int + trait D + + def foobar[D](in: D) = in.toString // error method parameter shadows some other type + type MySeq[D] = Seq[D] // error type member's parameter shadows some other type + + class Foo[T](t: T): // error class parameter shadows some other type + def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter + + // even deeply nested... + class C[M[List[_]]] // error + type E[M[List[_]]] = Int // error + def foo[N[M[List[_]]]] = ??? // error + + // ...but not between type parameters in the same list + class F[A, M[N[A]]] + type G[A, M[L[A]]] = Int + def bar[A, N[M[L[A]]]] = ??? + def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg-custom-args/fatal-warnings/i17613b.check b/tests/neg-custom-args/fatal-warnings/i17613b.check new file mode 100644 index 000000000000..ed8ed45e42eb --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613b.check @@ -0,0 +1,44 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:9:13 ---------------------------------------------- +9 | def foobar[ImTrait](in: D) = in.toString // error + | ^^^^^^^ + | Type parameter ImTrait for method foobar shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:10:13 --------------------------------------------- +10 | type MySeq[ImTrait] = Seq[D] // error + | ^^^^^^^ + | Type parameter ImTrait for type MySeq shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:12:14 --------------------------------------------- +12 | def foobar2[ImClass](in: D) = in.toString // error + | ^^^^^^^ + | Type parameter ImClass for method foobar2 shadows the type defined by class ImClass in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:13:14 --------------------------------------------- +13 | type MySeq2[ImClass] = Seq[D] // error + | ^^^^^^^ + | Type parameter ImClass for type MySeq2 shadows the type defined by class ImClass in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:15:12 --------------------------------------------- +15 | class Foo[T](t: T): // error class parameter shadows some other type + | ^ + | Type parameter T for class Foo shadows the type defined by type T in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:19:15 --------------------------------------------- +19 | def intType[List1](x: T) = x.toString() // error + | ^^^^^ + | Type parameter List1 for method intType shadows an explicitly renamed type : List1 +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- +23 | class C[M[List[_]]] // error List not renamed here + | ^^^^^^^ + | Type parameter List for class C shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:24:11 --------------------------------------------- +24 | type E[M[Int[_]]] = Int // error + | ^^^^^^ + | Type parameter Int for type E shadows the type defined by class Int in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:26:14 --------------------------------------------- +26 | def foo[N[M[List[_]]]] = // error + | ^^^^^^^ + | Type parameter List for method foo shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:29:11 --------------------------------------------- +29 | type Z[ImClassR] = Int // error + | ^^^^^^^^ + | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:30:18 --------------------------------------------- +30 | class InnerCl[ImClassR] // error + | ^^^^^^^^ + | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala new file mode 100644 index 000000000000..d2c1f334dd31 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala @@ -0,0 +1,33 @@ +// scalac: -Xlint:type-parameter-shadow + +object i17613b: + import importTry._ + class B: + type T = Int + trait D + + def foobar[ImTrait](in: D) = in.toString // error + type MySeq[ImTrait] = Seq[D] // error + + def foobar2[ImClass](in: D) = in.toString // error + type MySeq2[ImClass] = Seq[D] // error + + class Foo[T](t: T): // error class parameter shadows some other type + import scala.collection.immutable.{List => List1} + def bar[List](w: T) = w.toString // no warning due to the explicit import renaming + + def intType[List1](x: T) = x.toString() // error + + type Y[List] = Int // no warning + + class C[M[List[_]]] // error List not renamed here + type E[M[Int[_]]] = Int // error + + def foo[N[M[List[_]]]] = // error + import importTry.{ImClass => ImClassR} + def inner[ImClass] = // no warning + type Z[ImClassR] = Int // error + class InnerCl[ImClassR] // error + 5 + + def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala b/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file From f782a66bfc8385cc6714e4d5f17c0fbf7dd5a592 Mon Sep 17 00:00:00 2001 From: Carl Date: Wed, 7 Jun 2023 04:03:20 +0200 Subject: [PATCH 056/134] Move out miniphase [Cherry-picked 1f15b297b3d4579a75b55c1c2c2bce5558013d1e] --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/transform/CheckShadowing.scala | 28 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 743aca5bf90a..5444bc3f41b6 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,8 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper, new CheckShadowing) :: // Check for unused and shadowing elements + List(new CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckShadowing) :: // Check for shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala index 1c575fdc89a1..90834bf5441e 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -158,8 +158,8 @@ class CheckShadowing extends MiniPhase: override def traverse(tree: tpd.Tree)(using Context): Unit = tree match case t:tpd.Import => - shadowingDataApply(sd => sd.registerImport(t)) - traverseChildren(tree) + val newCtx = shadowingDataApply(sd => sd.registerImport(t)) + traverseChildren(tree)(using newCtx) case _ => traverseChildren(tree) @@ -180,9 +180,9 @@ object CheckShadowing: private val renamedImports = MutStack[MutMap[SimpleName, Name]]() // original name -> renamed name private val typeParamCandidates = MutMap[Symbol, Seq[tpd.TypeDef]]().withDefaultValue(Seq()) - private val shadowedTypeDefs = MutSet[TypeParamShadowWarning]() + private val typeParamShadowWarnings = MutSet[TypeParamShadowWarning]() - private val shadowedPrivateDefs = MutSet[PrivateShadowWarning]() + private val privateShadowWarnings = MutSet[PrivateShadowWarning]() def inNewScope()(using Context) = explicitsImports.push(MutSet()) @@ -194,7 +194,6 @@ object CheckShadowing: /** Register the Root imports (at once per compilation unit)*/ def registerRootImports()(using Context) = - ctx.definitions.rootImportTypes.foreach(rimp => println()) val langPackageName = ctx.definitions.JavaLangPackageVal.name.toSimpleName // excludes lang package rootImports.addAll(ctx.definitions.rootImportTypes.withFilter(_.name.toSimpleName != langPackageName).flatMap(_.typeMembers)) @@ -222,7 +221,7 @@ object CheckShadowing: .orElse(lookForUnitShadowedType(sym)) shadowedType.foreach(shadowed => if !renamedImports.exists(_.contains(shadowed.name.toSimpleName)) then - shadowedTypeDefs += TypeParamShadowWarning(typeDef.srcPos, typeDef.symbol, parent, shadowed) + typeParamShadowWarnings += TypeParamShadowWarning(typeDef.srcPos, typeDef.symbol, parent, shadowed) ) }) @@ -247,7 +246,7 @@ object CheckShadowing: /** Register if the valDef is a private declaration that shadows an inherited field */ def registerPrivateShadows(valDef: tpd.ValDef)(using Context): Unit = lookForShadowedField(valDef.symbol).foreach(shadowedField => - shadowedPrivateDefs += PrivateShadowWarning(valDef.startPos, valDef.symbol, shadowedField) + privateShadowWarnings += PrivateShadowWarning(valDef.startPos, valDef.symbol, shadowedField) ) private def lookForShadowedField(symDecl: Symbol)(using Context): Option[Symbol] = @@ -266,18 +265,17 @@ object CheckShadowing: /** Get the shadowing analysis's result */ def getShadowingResult(using Context): ShadowResult = - - val privateShadowWarnings: List[ShadowWarning] = + val privateWarnings: List[ShadowWarning] = if ctx.settings.XlintHas.privateShadow then - shadowedPrivateDefs.toList + privateShadowWarnings.toList else Nil - val typeParamShadowWarnings: List[ShadowWarning] = + val typeParamWarnings: List[ShadowWarning] = if ctx.settings.XlintHas.typeParameterShadow then - shadowedTypeDefs.toList + typeParamShadowWarnings.toList else Nil - ShadowResult(privateShadowWarnings ++ typeParamShadowWarnings) + ShadowResult(privateWarnings ++ typeParamWarnings) extension (sym: Symbol) /** Given an import and accessibility, return the import's symbol that matches import<->this symbol */ @@ -285,8 +283,8 @@ object CheckShadowing: val tpd.Import(qual, sels) = imp val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) - - sels.find(is => is.rename.toSimpleName == sym.name.toSimpleName).map(_.symbol) + sels + .find(is => is.rename.toSimpleName == sym.name.toSimpleName).map(_.symbol) .orElse(typeSelections.map(_.symbol).find(sd => sd.name == sym.name)) .orElse(simpleSelections.map(_.symbol).find(sd => sd.name == sym.name)) From 76b797f0a8e9bee9c55ec21daf0bb965615fca71 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:10:26 +0200 Subject: [PATCH 057/134] Fix Exception in CheckUnused isOverriden() helper [Cherry-picked 175d4f354ed848ed4fa7b6f63d150441667d0d5e][modified] --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +-- .../dotty/tools/dotc/config/ScalaSettings.scala | 7 ++----- .../dotty/tools/dotc/transform/CheckUnused.scala | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 5444bc3f41b6..6727fc4e91c0 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,8 +35,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper) :: // Check for unused elements - List(new CheckShadowing) :: // Check for shadowing elements + List(new CheckShadowing, new CheckUnused.PostTyper) :: // Check for unused elements // Check for shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 162c765926dd..1862298017aa 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -313,7 +313,6 @@ private sealed trait XSettings: helpArg = "advanced warning", descr = "Enable or disable specific `lint` warnings", choices = List( - ChoiceWithHelp("nowarn", ""), ChoiceWithHelp("all", ""), ChoiceWithHelp("private-shadow", "Warn if a private field or class parameter shadows a superclass field"), ChoiceWithHelp("type-parameter-shadow", "Warn when a type parameter shadows a type already in the scope"), @@ -322,10 +321,8 @@ private sealed trait XSettings: ) object XlintHas: - def isChoiceSet(s: String)(using Context) = Xlint.value.pipe(us => us.contains(s)) - def allOr(s: String)(using Context) = Xlint.value.pipe(us => us.contains("all") || us.contains(s)) - def nowarn(using Context) = allOr("nowarn") - + def allOr(s: String)(using Context) = + Xlint.value.pipe(us => us.contains("all") || us.contains(s)) def privateShadow(using Context) = allOr("private-shadow") def typeParameterShadow(using Context) = diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a3dbbfb202e7..092645bfa9fb 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -645,6 +645,19 @@ object CheckUnused: imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) ) + /** Returns some inherited symbol with the same type and name as the given "symDecl" */ + private def lookForInheritedDecl(symDecl: Symbol)(using Context): Option[Symbol] = + val symDeclType = symDecl.info + val bClasses = symDecl.owner.info.baseClasses + bClasses match + case _ :: inherited => + inherited + .map(classSymbol => symDecl.denot.matchingDecl(classSymbol, symDeclType)) + .find(sym => sym.name == symDecl.name) + case Nil => + None + + extension (tree: ImportSelector) def boundTpe: Type = tree.bound match { case untpd.TypedSplice(tree1) => tree1.tpe @@ -719,8 +732,7 @@ object CheckUnused: /** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ private def isOverriden(using Context): Boolean = - sym.is(Flags.Override) || - (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) + sym.is(Flags.Override) || lookForInheritedDecl(sym).isDefined end extension From d09614a48fbc2638905fb487ef67bd26724ed301 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 20 Jun 2023 11:16:27 +0200 Subject: [PATCH 058/134] Warn instead of fail for invalid -Xlint args [Cherry-picked 07f65819ac23d618f5f363b9f0fe28f11bd3c84f] --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../dotty/tools/dotc/config/ScalaSettings.scala | 2 +- .../src/dotty/tools/dotc/config/Settings.scala | 16 ++++++++++++++-- .../tools/dotc/transform/CheckShadowing.scala | 4 ++-- .../dotty/tools/dotc/transform/CheckUnused.scala | 4 ++-- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 6727fc4e91c0..ff701727c4ad 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckShadowing, new CheckUnused.PostTyper) :: // Check for unused elements // Check for shadowing elements + List(new CheckUnused.PostTyper, new CheckShadowing) :: // Check for unused elements and shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 1862298017aa..68671755df37 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -308,7 +308,7 @@ private sealed trait XSettings: val XmacroSettings: Setting[List[String]] = MultiStringSetting("-Xmacro-settings", "setting1,setting2,..settingN", "List of settings which exposed to the macros") - val Xlint: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( + val Xlint: Setting[List[ChoiceWithHelp[String]]] = UncompleteMultiChoiceHelpSetting( name = "-Xlint", helpArg = "advanced warning", descr = "Enable or disable specific `lint` warnings", diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index 34e5582e8a91..d992f5bdf2ee 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -62,6 +62,7 @@ object Settings: prefix: String = "", aliases: List[String] = Nil, depends: List[(Setting[?], Any)] = Nil, + ignoreInvalidArgs: Boolean = false, propertyClass: Option[Class[?]] = None)(private[Settings] val idx: Int) { private var changed: Boolean = false @@ -104,8 +105,16 @@ object Settings: def fail(msg: String, args: List[String]) = ArgsSummary(sstate, args, errors :+ msg, warnings) + def warn(msg: String, args: List[String]) = + ArgsSummary(sstate, args, errors, warnings :+ msg) + def missingArg = - fail(s"missing argument for option $name", args) + val msg = s"missing argument for option $name" + if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) else fail(msg, args) + + def invalidChoices(invalid: List[String]) = + val msg = s"invalid choice(s) for $name: ${invalid.mkString(",")}" + if ignoreInvalidArgs then warn(msg + ", the tag was ignored", args) else fail(msg, args) def setBoolean(argValue: String, args: List[String]) = if argValue.equalsIgnoreCase("true") || argValue.isEmpty then update(true, args) @@ -144,7 +153,7 @@ object Settings: choices match case Some(valid) => strings.filterNot(valid.contains) match case Nil => update(strings, args) - case invalid => fail(s"invalid choice(s) for $name: ${invalid.mkString(",")}", args) + case invalid => invalidChoices(invalid) case _ => update(strings, args) case (StringTag, _) if argRest.nonEmpty || choices.exists(_.contains("")) => setString(argRest, args) @@ -287,6 +296,9 @@ object Settings: def MultiChoiceHelpSetting(name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases)) + def UncompleteMultiChoiceHelpSetting(name: String, helpArg: String, descr: String, choices: List[ChoiceWithHelp[String]], default: List[ChoiceWithHelp[String]], aliases: List[String] = Nil): Setting[List[ChoiceWithHelp[String]]] = + publish(Setting(name, descr, default, helpArg, Some(choices), aliases = aliases, ignoreInvalidArgs = true)) + def IntSetting(name: String, descr: String, default: Int, aliases: List[String] = Nil): Setting[Int] = publish(Setting(name, descr, default, aliases = aliases)) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala index 90834bf5441e..26539bfb9b88 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -83,7 +83,7 @@ class CheckShadowing extends MiniPhase: ctx override def prepareForOther(tree: tpd.Tree)(using Context): Context = - importTraverser(tree.symbol).traverse(tree) + importTraverser.traverse(tree) ctx override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = @@ -152,7 +152,7 @@ class CheckShadowing extends MiniPhase: end nestedTypeTraverser // To reach the imports during a miniphase traversal - private def importTraverser(parent: Symbol) = new TreeTraverser: + private def importTraverser = new TreeTraverser: import tpd._ override def traverse(tree: tpd.Tree)(using Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 092645bfa9fb..6e91af4fe2e8 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -295,9 +295,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke case UnusedSymbol(t, _, WarnTypes.PatVars) => report.warning(s"unused pattern variable", t) case UnusedSymbol(t, _, WarnTypes.UnsetLocals) => - report.warning(s"unset local variable", t) + report.warning(s"unset local variable, consider using an immutable val instead", t) case UnusedSymbol(t, _, WarnTypes.UnsetPrivates) => - report.warning(s"unset private variable", t) + report.warning(s"unset private variable, consider using an immutable val instead", t) } end CheckUnused From ce9003d0417183375d414c16ac408254e5b76b4e Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 20 Jun 2023 18:09:51 +0200 Subject: [PATCH 059/134] Lint MegaPhase after ExtractSemanticDB [Cherry-picked 297183cba133ee98de3cb8aa37cabc9db2d05034] --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index ff701727c4ad..1383017b0e2a 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,10 +35,10 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper, new CheckShadowing) :: // Check for unused elements and shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files + List(new CheckUnused.PostTyper, new CheckShadowing) :: // Check for unused elements and shadowing elements List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks From a6e3d7e111566e45c52c2f134502594c280c5006 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 26 Sep 2023 01:02:11 +0200 Subject: [PATCH 060/134] Adjust export and synth. case class constructor false-positive cases & Add Tests [Cherry-picked 46f2db6af69063d09538f0da1a3a9c5ba5f871d1] --- .../tools/dotc/transform/CheckShadowing.scala | 12 ++--- .../dotty/tools/dotc/CompilationTests.scala | 1 + .../fatal-warnings/i17612b.check | 32 ++++++++++++++ .../fatal-warnings/i17612b/i17612b.scala | 44 +++++++++++++++++++ .../fatal-warnings/i17612b/importTry.scala | 5 +++ .../fatal-warnings/i17613b.check | 40 +++++++++++------ .../fatal-warnings/i17613b/i17613b.scala | 11 +++++ tests/pos/i16339.scala | 1 + 8 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i17612b.check create mode 100644 tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala index 26539bfb9b88..68dce87db388 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -116,13 +116,15 @@ class CheckShadowing extends MiniPhase: tree override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = - if tree.symbol.is(Param) && !tree.symbol.owner.isConstructor then // Do not register for constructors the work is done for the Class owned equivalent TypeDef + if tree.symbol.is(Param) && isValidTypeParamOwner(tree.symbol.owner) then // Do not register for constructors the work is done for the Class owned equivalent TypeDef shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol.owner)(using ctx.outer)) if tree.symbol.isAliasType then // No need to start outer here, because the TypeDef reached here it's already the parent shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol)(using ctx)) tree // Helpers : + private def isValidTypeParamOwner(owner: Symbol)(using Context): Boolean = + !owner.isConstructor && !owner.is(Synthetic) && !owner.is(Exported) private def reportShadowing(res: ShadowingData.ShadowResult)(using Context): Unit = res.warnings.sortBy(w => (w.pos.line, w.pos.startPos.column))(using Ordering[(Int, Int)]).foreach { s => @@ -211,7 +213,7 @@ object CheckShadowing: val actual = typeParamCandidates.getOrElseUpdate(parent, Seq()) typeParamCandidates.update(parent, actual.+:(typeDef)) - /** Compute if there is some TypeParam shadowing and register if it is the case*/ + /** Compute if there is some TypeParam shadowing and register if it is the case */ def computeTypeParamShadowsFor(parent: Symbol)(using Context): Unit = typeParamCandidates(parent).foreach(typeDef => { val sym = typeDef.symbol @@ -230,7 +232,7 @@ object CheckShadowing: private def lookForImportedShadowedType(symbol: Symbol)(using Context): Option[Symbol] = explicitsImports - .flatMap(_.flatMap(imp => symbol.isInImport(imp))) + .flatMap(_.flatMap(imp => symbol.isAnImportedType(imp))) .headOption private def lookForUnitShadowedType(symbol: Symbol)(using Context): Option[Symbol] = @@ -278,8 +280,8 @@ object CheckShadowing: ShadowResult(privateWarnings ++ typeParamWarnings) extension (sym: Symbol) - /** Given an import and accessibility, return the import's symbol that matches import<->this symbol */ - private def isInImport(imp: tpd.Import)(using Context): Option[Symbol] = + /** Looks after any type import symbol in the given import that matches this symbol */ + private def isAnImportedType(imp: tpd.Import)(using Context): Option[Symbol] = val tpd.Import(qual, sels) = imp val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c998dbec6721..c794b100bb42 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -33,6 +33,7 @@ class CompilationTests { compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init")), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), + compileFilesInDir("tests/pos", defaultOptions.and("-Xlint:private-shadow", "-Xlint:type-parameter-shadow")), compileFile("tests/pos-special/sourcepath/outer/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), compileFilesInDir("tests/pos-scala2", defaultOptions.and("-source", "3.0-migration")), compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")), diff --git a/tests/neg-custom-args/fatal-warnings/i17612b.check b/tests/neg-custom-args/fatal-warnings/i17612b.check new file mode 100644 index 000000000000..4d7215758fe0 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612b.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:15 --------------------------------------------- +21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value x in class Derived shadows field x inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:33 --------------------------------------------- +21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value y in class Derived shadows field y inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:23:2 ---------------------------------------------- +23 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + | ^ + | value shadowed2 in class Derived shadows field shadowed2 inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:24:2 ---------------------------------------------- +24 | private[this] val shadowed3 = 3 + 3 // error + | ^ + | value shadowed3 in class Derived shadows field shadowed3 inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:26:2 ---------------------------------------------- +26 | private val shadowed5 = 5 + 5 // error + | ^ + | value shadowed5 in class Derived shadows field shadowed5 inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:20 --------------------------------------------- +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value x in class UnderDerived shadows field x inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:28 --------------------------------------------- +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value y in class UnderDerived shadows field y inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:36 --------------------------------------------- +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value z in class UnderDerived shadows field z inherited from trait Base diff --git a/tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala b/tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala new file mode 100644 index 000000000000..95964edbb197 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala @@ -0,0 +1,44 @@ +// scalac: -Xlint:private-shadow + +object i17612b: + + trait Base(var x: Int, val y: Int, var z: Int): + var shadowed2 = 2 + val shadowed3 = 3 + val shadowed4 = 4 + protected var shadowed5 = 5 + + val notShadowed = -1 + private val notShadowed2 = -2 + val notShadowedbyLambda = -2 + + def increment(): Unit = + x = x + 1 + + trait BaseB + trait BaseC(var x2: Int) + + class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + private def hello() = 4 + private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + private[this] val shadowed3 = 3 + 3 // error + + private val shadowed5 = 5 + 5 // error + private val notShadowed2 = -4 + + val lambda: Int => Int => Int = + notShadowedbyLambda => + notShadowedbyLambda => + notShadowedbyLambda * 2 + + def inFunctionScope() = + val notShadowed = -2 // OK + -2 + + override def toString = + s"x : ${x.toString}, y : ${y.toString}" + + class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + + def main(args: Array[String]) = + val derived = new Derived(1, 1, 1, 1) diff --git a/tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala b/tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i17613b.check b/tests/neg-custom-args/fatal-warnings/i17613b.check index ed8ed45e42eb..2bde162ac51e 100644 --- a/tests/neg-custom-args/fatal-warnings/i17613b.check +++ b/tests/neg-custom-args/fatal-warnings/i17613b.check @@ -14,31 +14,43 @@ 13 | type MySeq2[ImClass] = Seq[D] // error | ^^^^^^^ | Type parameter ImClass for type MySeq2 shadows the type defined by class ImClass in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:15:12 --------------------------------------------- -15 | class Foo[T](t: T): // error class parameter shadows some other type +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:16:24 --------------------------------------------- +16 | type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error + | ^^^^^^^ + | Type parameter ImTrait for type TypeLambda shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:17:21 --------------------------------------------- +17 | type PolyFun[A] = [ImTrait] => ImTrait => B // error + | ^^^^^^^ + | Type parameter ImTrait for type PolyFun shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- +23 | class Foo[T](t: T): // error class parameter shadows some other type | ^ | Type parameter T for class Foo shadows the type defined by type T in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:19:15 --------------------------------------------- -19 | def intType[List1](x: T) = x.toString() // error +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:27:15 --------------------------------------------- +27 | def intType[List1](x: T) = x.toString() // error | ^^^^^ | Type parameter List1 for method intType shadows an explicitly renamed type : List1 --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- -23 | class C[M[List[_]]] // error List not renamed here +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:32:10 --------------------------------------------- +32 | given [Int]: Ordering[Int]() // error + | ^^^ + | Type parameter Int for method given_Ordering_Int shadows the type defined by class Int in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:34:12 --------------------------------------------- +34 | class C[M[List[_]]] // error List not renamed here | ^^^^^^^ | Type parameter List for class C shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:24:11 --------------------------------------------- -24 | type E[M[Int[_]]] = Int // error +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:35:11 --------------------------------------------- +35 | type E[M[Int[_]]] = Int // error | ^^^^^^ | Type parameter Int for type E shadows the type defined by class Int in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:26:14 --------------------------------------------- -26 | def foo[N[M[List[_]]]] = // error +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:37:14 --------------------------------------------- +37 | def foo[N[M[List[_]]]] = // error | ^^^^^^^ | Type parameter List for method foo shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:29:11 --------------------------------------------- -29 | type Z[ImClassR] = Int // error +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:40:11 --------------------------------------------- +40 | type Z[ImClassR] = Int // error | ^^^^^^^^ | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:30:18 --------------------------------------------- -30 | class InnerCl[ImClassR] // error +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:41:18 --------------------------------------------- +41 | class InnerCl[ImClassR] // error | ^^^^^^^^ | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala index d2c1f334dd31..9eeef7c29997 100644 --- a/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala +++ b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala @@ -12,6 +12,14 @@ object i17613b: def foobar2[ImClass](in: D) = in.toString // error type MySeq2[ImClass] = Seq[D] // error + given [A]: Ordering[Int]() + type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error + type PolyFun[A] = [ImTrait] => ImTrait => B // error + type MatchType[A] = A match { + case String => Int + case ImTrait => Boolean + } + class Foo[T](t: T): // error class parameter shadows some other type import scala.collection.immutable.{List => List1} def bar[List](w: T) = w.toString // no warning due to the explicit import renaming @@ -20,6 +28,9 @@ object i17613b: type Y[List] = Int // no warning + given [A]: Ordering[A]() + given [Int]: Ordering[Int]() // error + class C[M[List[_]]] // error List not renamed here type E[M[Int[_]]] = Int // error diff --git a/tests/pos/i16339.scala b/tests/pos/i16339.scala index 49924548bb3f..95062f516354 100644 --- a/tests/pos/i16339.scala +++ b/tests/pos/i16339.scala @@ -2,6 +2,7 @@ sealed trait Get[X, +X2 <: X] case class Bar[Y, Y2 <: Y](value: Y2) extends Get[Y, Y2] + class Test: def t1[Z, Z2 <: Z](get: Get[Z, Z2]) = get match case Bar(_) => From c431d612f4824605552f93bcce6d6ff1988fd9ec Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 26 Sep 2023 01:21:10 +0200 Subject: [PATCH 061/134] Move tests from neg-custom-args/fatal-warnings to neg [Cherry-picked fb8426a4a495074e24a2f251c423128e78f8781b] --- tests/neg/i17612a.check | 32 ++++++++++++++++++ tests/neg/i17612a.scala | 42 +++++++++++++++++++++++ tests/neg/i17612b.check | 32 ++++++++++++++++++ tests/neg/i17612b/i17612b.scala | 44 ++++++++++++++++++++++++ tests/neg/i17612b/importTry.scala | 5 +++ tests/neg/i17613a.check | 28 ++++++++++++++++ tests/neg/i17613a.scala | 23 +++++++++++++ tests/neg/i17613b.check | 56 +++++++++++++++++++++++++++++++ tests/neg/i17613b/i17613b.scala | 44 ++++++++++++++++++++++++ tests/neg/i17613b/importTry.scala | 5 +++ 10 files changed, 311 insertions(+) create mode 100644 tests/neg/i17612a.check create mode 100644 tests/neg/i17612a.scala create mode 100644 tests/neg/i17612b.check create mode 100644 tests/neg/i17612b/i17612b.scala create mode 100644 tests/neg/i17612b/importTry.scala create mode 100644 tests/neg/i17613a.check create mode 100644 tests/neg/i17613a.scala create mode 100644 tests/neg/i17613b.check create mode 100644 tests/neg/i17613b/i17613b.scala create mode 100644 tests/neg/i17613b/importTry.scala diff --git a/tests/neg/i17612a.check b/tests/neg/i17612a.check new file mode 100644 index 000000000000..fad897b7c5f8 --- /dev/null +++ b/tests/neg/i17612a.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:15 ----------------------------------------------------- +18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value x in class Derived shadows field x inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:24 ----------------------------------------------------- +18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value y in class Derived shadows field y inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:20:2 ------------------------------------------------------ +20 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + | ^ + | value shadowed2 in class Derived shadows field shadowed2 inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:21:2 ------------------------------------------------------ +21 | private[this] val shadowed3 = 3 + 3 // error + | ^ + | value shadowed3 in class Derived shadows field shadowed3 inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:23:2 ------------------------------------------------------ +23 | private val shadowed5 = 5 + 5 // error + | ^ + | value shadowed5 in class Derived shadows field shadowed5 inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:20 ----------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value x in class UnderDerived shadows field x inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:28 ----------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value y in class UnderDerived shadows field y inherited from class Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:36 ----------------------------------------------------- +34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + | ^ + | value z in class UnderDerived shadows field z inherited from class Base diff --git a/tests/neg/i17612a.scala b/tests/neg/i17612a.scala new file mode 100644 index 000000000000..0fb6306b96cb --- /dev/null +++ b/tests/neg/i17612a.scala @@ -0,0 +1,42 @@ +// scalac: -Xlint:private-shadow + +object i17612a: + class Base(var x: Int, val y: Int, var z: Int): + var shadowed2 = 2 + val shadowed3 = 3 + val shadowed4 = 4 + protected var shadowed5 = 5 + //var shadowed6 = 6 + + val notShadowed = -1 + private val notShadowed2 = -2 + //val fatalOverride = 0 + + def increment(): Unit = + x = x + 1 + + class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + private def hello() = 4 + private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + private[this] val shadowed3 = 3 + 3 // error + //private[Derived] val fatalOverride = 0 // value fatalOverride of type Int has weaker access privileges; it should be public + private val shadowed5 = 5 + 5 // error + private val notShadowed2 = -4 + //protected var shadowed6 = 6 + 6 // variable shadowed6 of type Int has weaker access privileges; it should be public + + def inFunctionScope() = + val notShadowed = -2 // OK + -2 + + override def toString = + s"x : ${x.toString}, y : ${y.toString}" + + class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error + + def main(args: Array[String]) = + val derived = new Derived(1, 1, 1) + println(derived.toString) // yields x: '1', as expected + derived.increment() + println(derived.toString) // still x: '1', probably unexpected, for y it never prints the super value, less surprising + println(derived.shadowed2) + println(derived.shadowed3) \ No newline at end of file diff --git a/tests/neg/i17612b.check b/tests/neg/i17612b.check new file mode 100644 index 000000000000..4d7215758fe0 --- /dev/null +++ b/tests/neg/i17612b.check @@ -0,0 +1,32 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:15 --------------------------------------------- +21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value x in class Derived shadows field x inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:33 --------------------------------------------- +21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + | ^ + | value y in class Derived shadows field y inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:23:2 ---------------------------------------------- +23 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + | ^ + | value shadowed2 in class Derived shadows field shadowed2 inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:24:2 ---------------------------------------------- +24 | private[this] val shadowed3 = 3 + 3 // error + | ^ + | value shadowed3 in class Derived shadows field shadowed3 inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:26:2 ---------------------------------------------- +26 | private val shadowed5 = 5 + 5 // error + | ^ + | value shadowed5 in class Derived shadows field shadowed5 inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:20 --------------------------------------------- +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value x in class UnderDerived shadows field x inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:28 --------------------------------------------- +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value y in class UnderDerived shadows field y inherited from trait Base +-- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:36 --------------------------------------------- +41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + | ^ + | value z in class UnderDerived shadows field z inherited from trait Base diff --git a/tests/neg/i17612b/i17612b.scala b/tests/neg/i17612b/i17612b.scala new file mode 100644 index 000000000000..95964edbb197 --- /dev/null +++ b/tests/neg/i17612b/i17612b.scala @@ -0,0 +1,44 @@ +// scalac: -Xlint:private-shadow + +object i17612b: + + trait Base(var x: Int, val y: Int, var z: Int): + var shadowed2 = 2 + val shadowed3 = 3 + val shadowed4 = 4 + protected var shadowed5 = 5 + + val notShadowed = -1 + private val notShadowed2 = -2 + val notShadowedbyLambda = -2 + + def increment(): Unit = + x = x + 1 + + trait BaseB + trait BaseC(var x2: Int) + + class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y + private def hello() = 4 + private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) + private[this] val shadowed3 = 3 + 3 // error + + private val shadowed5 = 5 + 5 // error + private val notShadowed2 = -4 + + val lambda: Int => Int => Int = + notShadowedbyLambda => + notShadowedbyLambda => + notShadowedbyLambda * 2 + + def inFunctionScope() = + val notShadowed = -2 // OK + -2 + + override def toString = + s"x : ${x.toString}, y : ${y.toString}" + + class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error + + def main(args: Array[String]) = + val derived = new Derived(1, 1, 1, 1) diff --git a/tests/neg/i17612b/importTry.scala b/tests/neg/i17612b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg/i17612b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file diff --git a/tests/neg/i17613a.check b/tests/neg/i17613a.check new file mode 100644 index 000000000000..b0aeb85101a1 --- /dev/null +++ b/tests/neg/i17613a.check @@ -0,0 +1,28 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:8:13 ------------------------------------------------------ +8 | def foobar[D](in: D) = in.toString // error method parameter shadows some other type + | ^ + | Type parameter D for method foobar shadows the type defined by trait D in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:9:13 ------------------------------------------------------ +9 | type MySeq[D] = Seq[D] // error type member's parameter shadows some other type + | ^ + | Type parameter D for type MySeq shadows the type defined by trait D in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:11:12 ----------------------------------------------------- +11 | class Foo[T](t: T): // error class parameter shadows some other type + | ^ + | Type parameter T for class Foo shadows the type defined by type T in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:12:11 ----------------------------------------------------- +12 | def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter + | ^ + | Type parameter T for method bar shadows the type defined by type T in class Foo +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:15:12 ----------------------------------------------------- +15 | class C[M[List[_]]] // error + | ^^^^^^^ + | Type parameter List for class C shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:16:11 ----------------------------------------------------- +16 | type E[M[List[_]]] = Int // error + | ^^^^^^^ + | Type parameter List for type E shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:17:14 ----------------------------------------------------- +17 | def foo[N[M[List[_]]]] = ??? // error + | ^^^^^^^ + | Type parameter List for method foo shadows the type defined by type List in package scala diff --git a/tests/neg/i17613a.scala b/tests/neg/i17613a.scala new file mode 100644 index 000000000000..4639bd4b5053 --- /dev/null +++ b/tests/neg/i17613a.scala @@ -0,0 +1,23 @@ +// scalac: -Xlint:type-parameter-shadow + +object i17613a: + class B: + type T = Int + trait D + + def foobar[D](in: D) = in.toString // error method parameter shadows some other type + type MySeq[D] = Seq[D] // error type member's parameter shadows some other type + + class Foo[T](t: T): // error class parameter shadows some other type + def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter + + // even deeply nested... + class C[M[List[_]]] // error + type E[M[List[_]]] = Int // error + def foo[N[M[List[_]]]] = ??? // error + + // ...but not between type parameters in the same list + class F[A, M[N[A]]] + type G[A, M[L[A]]] = Int + def bar[A, N[M[L[A]]]] = ??? + def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg/i17613b.check b/tests/neg/i17613b.check new file mode 100644 index 000000000000..2bde162ac51e --- /dev/null +++ b/tests/neg/i17613b.check @@ -0,0 +1,56 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:9:13 ---------------------------------------------- +9 | def foobar[ImTrait](in: D) = in.toString // error + | ^^^^^^^ + | Type parameter ImTrait for method foobar shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:10:13 --------------------------------------------- +10 | type MySeq[ImTrait] = Seq[D] // error + | ^^^^^^^ + | Type parameter ImTrait for type MySeq shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:12:14 --------------------------------------------- +12 | def foobar2[ImClass](in: D) = in.toString // error + | ^^^^^^^ + | Type parameter ImClass for method foobar2 shadows the type defined by class ImClass in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:13:14 --------------------------------------------- +13 | type MySeq2[ImClass] = Seq[D] // error + | ^^^^^^^ + | Type parameter ImClass for type MySeq2 shadows the type defined by class ImClass in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:16:24 --------------------------------------------- +16 | type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error + | ^^^^^^^ + | Type parameter ImTrait for type TypeLambda shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:17:21 --------------------------------------------- +17 | type PolyFun[A] = [ImTrait] => ImTrait => B // error + | ^^^^^^^ + | Type parameter ImTrait for type PolyFun shadows the type defined by trait ImTrait in object importTry +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- +23 | class Foo[T](t: T): // error class parameter shadows some other type + | ^ + | Type parameter T for class Foo shadows the type defined by type T in class B +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:27:15 --------------------------------------------- +27 | def intType[List1](x: T) = x.toString() // error + | ^^^^^ + | Type parameter List1 for method intType shadows an explicitly renamed type : List1 +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:32:10 --------------------------------------------- +32 | given [Int]: Ordering[Int]() // error + | ^^^ + | Type parameter Int for method given_Ordering_Int shadows the type defined by class Int in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:34:12 --------------------------------------------- +34 | class C[M[List[_]]] // error List not renamed here + | ^^^^^^^ + | Type parameter List for class C shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:35:11 --------------------------------------------- +35 | type E[M[Int[_]]] = Int // error + | ^^^^^^ + | Type parameter Int for type E shadows the type defined by class Int in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:37:14 --------------------------------------------- +37 | def foo[N[M[List[_]]]] = // error + | ^^^^^^^ + | Type parameter List for method foo shadows the type defined by type List in package scala +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:40:11 --------------------------------------------- +40 | type Z[ImClassR] = Int // error + | ^^^^^^^^ + | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR +-- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:41:18 --------------------------------------------- +41 | class InnerCl[ImClassR] // error + | ^^^^^^^^ + | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg/i17613b/i17613b.scala b/tests/neg/i17613b/i17613b.scala new file mode 100644 index 000000000000..9eeef7c29997 --- /dev/null +++ b/tests/neg/i17613b/i17613b.scala @@ -0,0 +1,44 @@ +// scalac: -Xlint:type-parameter-shadow + +object i17613b: + import importTry._ + class B: + type T = Int + trait D + + def foobar[ImTrait](in: D) = in.toString // error + type MySeq[ImTrait] = Seq[D] // error + + def foobar2[ImClass](in: D) = in.toString // error + type MySeq2[ImClass] = Seq[D] // error + + given [A]: Ordering[Int]() + type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error + type PolyFun[A] = [ImTrait] => ImTrait => B // error + type MatchType[A] = A match { + case String => Int + case ImTrait => Boolean + } + + class Foo[T](t: T): // error class parameter shadows some other type + import scala.collection.immutable.{List => List1} + def bar[List](w: T) = w.toString // no warning due to the explicit import renaming + + def intType[List1](x: T) = x.toString() // error + + type Y[List] = Int // no warning + + given [A]: Ordering[A]() + given [Int]: Ordering[Int]() // error + + class C[M[List[_]]] // error List not renamed here + type E[M[Int[_]]] = Int // error + + def foo[N[M[List[_]]]] = // error + import importTry.{ImClass => ImClassR} + def inner[ImClass] = // no warning + type Z[ImClassR] = Int // error + class InnerCl[ImClassR] // error + 5 + + def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg/i17613b/importTry.scala b/tests/neg/i17613b/importTry.scala new file mode 100644 index 000000000000..879f40ace356 --- /dev/null +++ b/tests/neg/i17613b/importTry.scala @@ -0,0 +1,5 @@ +object importTry: + + trait ImTrait + + class ImClass \ No newline at end of file From 830ed0fb2f92890c67f2869ce8a4accc924e895b Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 26 Sep 2023 01:31:27 +0200 Subject: [PATCH 062/134] Delete tests in old folder [Cherry-picked 2edd869323e2470599209e65e3f568fb698787f4] --- .../fatal-warnings/i17612a.check | 32 ----------- .../fatal-warnings/i17612a.scala | 42 -------------- .../fatal-warnings/i17612b.check | 32 ----------- .../fatal-warnings/i17612b/i17612b.scala | 44 --------------- .../fatal-warnings/i17612b/importTry.scala | 5 -- .../fatal-warnings/i17613a.check | 28 ---------- .../fatal-warnings/i17613a.scala | 23 -------- .../fatal-warnings/i17613b.check | 56 ------------------- .../fatal-warnings/i17613b/i17613b.scala | 44 --------------- .../fatal-warnings/i17613b/importTry.scala | 5 -- 10 files changed, 311 deletions(-) delete mode 100644 tests/neg-custom-args/fatal-warnings/i17612a.check delete mode 100644 tests/neg-custom-args/fatal-warnings/i17612a.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i17612b.check delete mode 100644 tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i17613a.check delete mode 100644 tests/neg-custom-args/fatal-warnings/i17613a.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i17613b.check delete mode 100644 tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala diff --git a/tests/neg-custom-args/fatal-warnings/i17612a.check b/tests/neg-custom-args/fatal-warnings/i17612a.check deleted file mode 100644 index fad897b7c5f8..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17612a.check +++ /dev/null @@ -1,32 +0,0 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:15 ----------------------------------------------------- -18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y - | ^ - | value x in class Derived shadows field x inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:24 ----------------------------------------------------- -18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y - | ^ - | value y in class Derived shadows field y inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:20:2 ------------------------------------------------------ -20 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) - | ^ - | value shadowed2 in class Derived shadows field shadowed2 inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:21:2 ------------------------------------------------------ -21 | private[this] val shadowed3 = 3 + 3 // error - | ^ - | value shadowed3 in class Derived shadows field shadowed3 inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:23:2 ------------------------------------------------------ -23 | private val shadowed5 = 5 + 5 // error - | ^ - | value shadowed5 in class Derived shadows field shadowed5 inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:20 ----------------------------------------------------- -34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error - | ^ - | value x in class UnderDerived shadows field x inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:28 ----------------------------------------------------- -34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error - | ^ - | value y in class UnderDerived shadows field y inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:36 ----------------------------------------------------- -34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error - | ^ - | value z in class UnderDerived shadows field z inherited from class Base diff --git a/tests/neg-custom-args/fatal-warnings/i17612a.scala b/tests/neg-custom-args/fatal-warnings/i17612a.scala deleted file mode 100644 index 0fb6306b96cb..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17612a.scala +++ /dev/null @@ -1,42 +0,0 @@ -// scalac: -Xlint:private-shadow - -object i17612a: - class Base(var x: Int, val y: Int, var z: Int): - var shadowed2 = 2 - val shadowed3 = 3 - val shadowed4 = 4 - protected var shadowed5 = 5 - //var shadowed6 = 6 - - val notShadowed = -1 - private val notShadowed2 = -2 - //val fatalOverride = 0 - - def increment(): Unit = - x = x + 1 - - class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y - private def hello() = 4 - private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) - private[this] val shadowed3 = 3 + 3 // error - //private[Derived] val fatalOverride = 0 // value fatalOverride of type Int has weaker access privileges; it should be public - private val shadowed5 = 5 + 5 // error - private val notShadowed2 = -4 - //protected var shadowed6 = 6 + 6 // variable shadowed6 of type Int has weaker access privileges; it should be public - - def inFunctionScope() = - val notShadowed = -2 // OK - -2 - - override def toString = - s"x : ${x.toString}, y : ${y.toString}" - - class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error - - def main(args: Array[String]) = - val derived = new Derived(1, 1, 1) - println(derived.toString) // yields x: '1', as expected - derived.increment() - println(derived.toString) // still x: '1', probably unexpected, for y it never prints the super value, less surprising - println(derived.shadowed2) - println(derived.shadowed3) \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i17612b.check b/tests/neg-custom-args/fatal-warnings/i17612b.check deleted file mode 100644 index 4d7215758fe0..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17612b.check +++ /dev/null @@ -1,32 +0,0 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:15 --------------------------------------------- -21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y - | ^ - | value x in class Derived shadows field x inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:33 --------------------------------------------- -21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y - | ^ - | value y in class Derived shadows field y inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:23:2 ---------------------------------------------- -23 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) - | ^ - | value shadowed2 in class Derived shadows field shadowed2 inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:24:2 ---------------------------------------------- -24 | private[this] val shadowed3 = 3 + 3 // error - | ^ - | value shadowed3 in class Derived shadows field shadowed3 inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:26:2 ---------------------------------------------- -26 | private val shadowed5 = 5 + 5 // error - | ^ - | value shadowed5 in class Derived shadows field shadowed5 inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:20 --------------------------------------------- -41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error - | ^ - | value x in class UnderDerived shadows field x inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:28 --------------------------------------------- -41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error - | ^ - | value y in class UnderDerived shadows field y inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:36 --------------------------------------------- -41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error - | ^ - | value z in class UnderDerived shadows field z inherited from trait Base diff --git a/tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala b/tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala deleted file mode 100644 index 95964edbb197..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala +++ /dev/null @@ -1,44 +0,0 @@ -// scalac: -Xlint:private-shadow - -object i17612b: - - trait Base(var x: Int, val y: Int, var z: Int): - var shadowed2 = 2 - val shadowed3 = 3 - val shadowed4 = 4 - protected var shadowed5 = 5 - - val notShadowed = -1 - private val notShadowed2 = -2 - val notShadowedbyLambda = -2 - - def increment(): Unit = - x = x + 1 - - trait BaseB - trait BaseC(var x2: Int) - - class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y - private def hello() = 4 - private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) - private[this] val shadowed3 = 3 + 3 // error - - private val shadowed5 = 5 + 5 // error - private val notShadowed2 = -4 - - val lambda: Int => Int => Int = - notShadowedbyLambda => - notShadowedbyLambda => - notShadowedbyLambda * 2 - - def inFunctionScope() = - val notShadowed = -2 // OK - -2 - - override def toString = - s"x : ${x.toString}, y : ${y.toString}" - - class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error - - def main(args: Array[String]) = - val derived = new Derived(1, 1, 1, 1) diff --git a/tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala b/tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala deleted file mode 100644 index 879f40ace356..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17612b/importTry.scala +++ /dev/null @@ -1,5 +0,0 @@ -object importTry: - - trait ImTrait - - class ImClass \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i17613a.check b/tests/neg-custom-args/fatal-warnings/i17613a.check deleted file mode 100644 index b0aeb85101a1..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17613a.check +++ /dev/null @@ -1,28 +0,0 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:8:13 ------------------------------------------------------ -8 | def foobar[D](in: D) = in.toString // error method parameter shadows some other type - | ^ - | Type parameter D for method foobar shadows the type defined by trait D in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:9:13 ------------------------------------------------------ -9 | type MySeq[D] = Seq[D] // error type member's parameter shadows some other type - | ^ - | Type parameter D for type MySeq shadows the type defined by trait D in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:11:12 ----------------------------------------------------- -11 | class Foo[T](t: T): // error class parameter shadows some other type - | ^ - | Type parameter T for class Foo shadows the type defined by type T in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:12:11 ----------------------------------------------------- -12 | def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter - | ^ - | Type parameter T for method bar shadows the type defined by type T in class Foo --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:15:12 ----------------------------------------------------- -15 | class C[M[List[_]]] // error - | ^^^^^^^ - | Type parameter List for class C shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:16:11 ----------------------------------------------------- -16 | type E[M[List[_]]] = Int // error - | ^^^^^^^ - | Type parameter List for type E shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:17:14 ----------------------------------------------------- -17 | def foo[N[M[List[_]]]] = ??? // error - | ^^^^^^^ - | Type parameter List for method foo shadows the type defined by type List in package scala diff --git a/tests/neg-custom-args/fatal-warnings/i17613a.scala b/tests/neg-custom-args/fatal-warnings/i17613a.scala deleted file mode 100644 index 4639bd4b5053..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17613a.scala +++ /dev/null @@ -1,23 +0,0 @@ -// scalac: -Xlint:type-parameter-shadow - -object i17613a: - class B: - type T = Int - trait D - - def foobar[D](in: D) = in.toString // error method parameter shadows some other type - type MySeq[D] = Seq[D] // error type member's parameter shadows some other type - - class Foo[T](t: T): // error class parameter shadows some other type - def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter - - // even deeply nested... - class C[M[List[_]]] // error - type E[M[List[_]]] = Int // error - def foo[N[M[List[_]]]] = ??? // error - - // ...but not between type parameters in the same list - class F[A, M[N[A]]] - type G[A, M[L[A]]] = Int - def bar[A, N[M[L[A]]]] = ??? - def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg-custom-args/fatal-warnings/i17613b.check b/tests/neg-custom-args/fatal-warnings/i17613b.check deleted file mode 100644 index 2bde162ac51e..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17613b.check +++ /dev/null @@ -1,56 +0,0 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:9:13 ---------------------------------------------- -9 | def foobar[ImTrait](in: D) = in.toString // error - | ^^^^^^^ - | Type parameter ImTrait for method foobar shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:10:13 --------------------------------------------- -10 | type MySeq[ImTrait] = Seq[D] // error - | ^^^^^^^ - | Type parameter ImTrait for type MySeq shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:12:14 --------------------------------------------- -12 | def foobar2[ImClass](in: D) = in.toString // error - | ^^^^^^^ - | Type parameter ImClass for method foobar2 shadows the type defined by class ImClass in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:13:14 --------------------------------------------- -13 | type MySeq2[ImClass] = Seq[D] // error - | ^^^^^^^ - | Type parameter ImClass for type MySeq2 shadows the type defined by class ImClass in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:16:24 --------------------------------------------- -16 | type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error - | ^^^^^^^ - | Type parameter ImTrait for type TypeLambda shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:17:21 --------------------------------------------- -17 | type PolyFun[A] = [ImTrait] => ImTrait => B // error - | ^^^^^^^ - | Type parameter ImTrait for type PolyFun shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- -23 | class Foo[T](t: T): // error class parameter shadows some other type - | ^ - | Type parameter T for class Foo shadows the type defined by type T in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:27:15 --------------------------------------------- -27 | def intType[List1](x: T) = x.toString() // error - | ^^^^^ - | Type parameter List1 for method intType shadows an explicitly renamed type : List1 --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:32:10 --------------------------------------------- -32 | given [Int]: Ordering[Int]() // error - | ^^^ - | Type parameter Int for method given_Ordering_Int shadows the type defined by class Int in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:34:12 --------------------------------------------- -34 | class C[M[List[_]]] // error List not renamed here - | ^^^^^^^ - | Type parameter List for class C shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:35:11 --------------------------------------------- -35 | type E[M[Int[_]]] = Int // error - | ^^^^^^ - | Type parameter Int for type E shadows the type defined by class Int in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:37:14 --------------------------------------------- -37 | def foo[N[M[List[_]]]] = // error - | ^^^^^^^ - | Type parameter List for method foo shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:40:11 --------------------------------------------- -40 | type Z[ImClassR] = Int // error - | ^^^^^^^^ - | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:41:18 --------------------------------------------- -41 | class InnerCl[ImClassR] // error - | ^^^^^^^^ - | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala b/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala deleted file mode 100644 index 9eeef7c29997..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala +++ /dev/null @@ -1,44 +0,0 @@ -// scalac: -Xlint:type-parameter-shadow - -object i17613b: - import importTry._ - class B: - type T = Int - trait D - - def foobar[ImTrait](in: D) = in.toString // error - type MySeq[ImTrait] = Seq[D] // error - - def foobar2[ImClass](in: D) = in.toString // error - type MySeq2[ImClass] = Seq[D] // error - - given [A]: Ordering[Int]() - type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error - type PolyFun[A] = [ImTrait] => ImTrait => B // error - type MatchType[A] = A match { - case String => Int - case ImTrait => Boolean - } - - class Foo[T](t: T): // error class parameter shadows some other type - import scala.collection.immutable.{List => List1} - def bar[List](w: T) = w.toString // no warning due to the explicit import renaming - - def intType[List1](x: T) = x.toString() // error - - type Y[List] = Int // no warning - - given [A]: Ordering[A]() - given [Int]: Ordering[Int]() // error - - class C[M[List[_]]] // error List not renamed here - type E[M[Int[_]]] = Int // error - - def foo[N[M[List[_]]]] = // error - import importTry.{ImClass => ImClassR} - def inner[ImClass] = // no warning - type Z[ImClassR] = Int // error - class InnerCl[ImClassR] // error - 5 - - def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala b/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala deleted file mode 100644 index 879f40ace356..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i17613b/importTry.scala +++ /dev/null @@ -1,5 +0,0 @@ -object importTry: - - trait ImTrait - - class ImClass \ No newline at end of file From 2f814302cc0e63f3de2aaddb502293f23b976681 Mon Sep 17 00:00:00 2001 From: Carl Date: Tue, 26 Sep 2023 11:20:20 +0200 Subject: [PATCH 063/134] Change options line for tests/neg folder [Cherry-picked 1db2afb8064d68219dd867bd9c597a387be49bcd] --- tests/neg/i17612a.check | 16 ++++++++-------- tests/neg/i17612a.scala | 2 +- tests/neg/i17612b.check | 16 ++++++++-------- tests/neg/i17612b/i17612b.scala | 2 +- tests/neg/i17613a.check | 14 +++++++------- tests/neg/i17613a.scala | 10 +++++----- tests/neg/i17613b.check | 28 ++++++++++++++-------------- tests/neg/i17613b/i17613b.scala | 4 ++-- 8 files changed, 46 insertions(+), 46 deletions(-) diff --git a/tests/neg/i17612a.check b/tests/neg/i17612a.check index fad897b7c5f8..04a5b07b6e33 100644 --- a/tests/neg/i17612a.check +++ b/tests/neg/i17612a.check @@ -1,32 +1,32 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:15 ----------------------------------------------------- +-- Error: tests/neg/i17612a.scala:18:15 -------------------------------------------------------------------------------- 18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y | ^ | value x in class Derived shadows field x inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:18:24 ----------------------------------------------------- +-- Error: tests/neg/i17612a.scala:18:24 -------------------------------------------------------------------------------- 18 | class Derived(x : Int, y: Int, z2: Int) extends Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y | ^ | value y in class Derived shadows field y inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:20:2 ------------------------------------------------------ +-- Error: tests/neg/i17612a.scala:20:2 --------------------------------------------------------------------------------- 20 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) | ^ | value shadowed2 in class Derived shadows field shadowed2 inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:21:2 ------------------------------------------------------ +-- Error: tests/neg/i17612a.scala:21:2 --------------------------------------------------------------------------------- 21 | private[this] val shadowed3 = 3 + 3 // error | ^ | value shadowed3 in class Derived shadows field shadowed3 inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:23:2 ------------------------------------------------------ +-- Error: tests/neg/i17612a.scala:23:2 --------------------------------------------------------------------------------- 23 | private val shadowed5 = 5 + 5 // error | ^ | value shadowed5 in class Derived shadows field shadowed5 inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:20 ----------------------------------------------------- +-- Error: tests/neg/i17612a.scala:34:20 -------------------------------------------------------------------------------- 34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error | ^ | value x in class UnderDerived shadows field x inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:28 ----------------------------------------------------- +-- Error: tests/neg/i17612a.scala:34:28 -------------------------------------------------------------------------------- 34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error | ^ | value y in class UnderDerived shadows field y inherited from class Base --- Error: tests/neg-custom-args/fatal-warnings/i17612a.scala:34:36 ----------------------------------------------------- +-- Error: tests/neg/i17612a.scala:34:36 -------------------------------------------------------------------------------- 34 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, y, z) // error // error // error | ^ | value z in class UnderDerived shadows field z inherited from class Base diff --git a/tests/neg/i17612a.scala b/tests/neg/i17612a.scala index 0fb6306b96cb..1145cd76edc0 100644 --- a/tests/neg/i17612a.scala +++ b/tests/neg/i17612a.scala @@ -1,4 +1,4 @@ -// scalac: -Xlint:private-shadow +//> using options -Xfatal-warnings -Xlint:private-shadow object i17612a: class Base(var x: Int, val y: Int, var z: Int): diff --git a/tests/neg/i17612b.check b/tests/neg/i17612b.check index 4d7215758fe0..75e8b7312833 100644 --- a/tests/neg/i17612b.check +++ b/tests/neg/i17612b.check @@ -1,32 +1,32 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:15 --------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:21:15 ------------------------------------------------------------------------ 21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y | ^ | value x in class Derived shadows field x inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:21:33 --------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:21:33 ------------------------------------------------------------------------ 21 | class Derived(x : Int, x3: Int, y: Int, z2: Int) extends BaseB, BaseC(x3), Base(x, y + 1, z2): // error // error / for x, y translated to private[this] x field & shadowing var Base.x, Base.y | ^ | value y in class Derived shadows field y inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:23:2 ---------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:23:2 ------------------------------------------------------------------------- 23 | private val shadowed2 = 2 + 2 // error (In Scala 2 we cannot do that got the warning) | ^ | value shadowed2 in class Derived shadows field shadowed2 inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:24:2 ---------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:24:2 ------------------------------------------------------------------------- 24 | private[this] val shadowed3 = 3 + 3 // error | ^ | value shadowed3 in class Derived shadows field shadowed3 inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:26:2 ---------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:26:2 ------------------------------------------------------------------------- 26 | private val shadowed5 = 5 + 5 // error | ^ | value shadowed5 in class Derived shadows field shadowed5 inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:20 --------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:41:20 ------------------------------------------------------------------------ 41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error | ^ | value x in class UnderDerived shadows field x inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:28 --------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:41:28 ------------------------------------------------------------------------ 41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error | ^ | value y in class UnderDerived shadows field y inherited from trait Base --- Error: tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala:41:36 --------------------------------------------- +-- Error: tests/neg/i17612b/i17612b.scala:41:36 ------------------------------------------------------------------------ 41 | class UnderDerived(x: Int, y: Int, z: Int) extends Derived(x, 1, y, z) // error // error // error | ^ | value z in class UnderDerived shadows field z inherited from trait Base diff --git a/tests/neg/i17612b/i17612b.scala b/tests/neg/i17612b/i17612b.scala index 95964edbb197..b59352f562d0 100644 --- a/tests/neg/i17612b/i17612b.scala +++ b/tests/neg/i17612b/i17612b.scala @@ -1,4 +1,4 @@ -// scalac: -Xlint:private-shadow +//> using options -Xfatal-warnings -Xlint:private-shadow object i17612b: diff --git a/tests/neg/i17613a.check b/tests/neg/i17613a.check index b0aeb85101a1..4721b786c82d 100644 --- a/tests/neg/i17613a.check +++ b/tests/neg/i17613a.check @@ -1,28 +1,28 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:8:13 ------------------------------------------------------ +-- Error: tests/neg/i17613a.scala:8:13 --------------------------------------------------------------------------------- 8 | def foobar[D](in: D) = in.toString // error method parameter shadows some other type | ^ | Type parameter D for method foobar shadows the type defined by trait D in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:9:13 ------------------------------------------------------ +-- Error: tests/neg/i17613a.scala:9:13 --------------------------------------------------------------------------------- 9 | type MySeq[D] = Seq[D] // error type member's parameter shadows some other type | ^ | Type parameter D for type MySeq shadows the type defined by trait D in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:11:12 ----------------------------------------------------- +-- Error: tests/neg/i17613a.scala:11:12 -------------------------------------------------------------------------------- 11 | class Foo[T](t: T): // error class parameter shadows some other type | ^ | Type parameter T for class Foo shadows the type defined by type T in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:12:11 ----------------------------------------------------- +-- Error: tests/neg/i17613a.scala:12:11 -------------------------------------------------------------------------------- 12 | def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter | ^ | Type parameter T for method bar shadows the type defined by type T in class Foo --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:15:12 ----------------------------------------------------- +-- Error: tests/neg/i17613a.scala:15:12 -------------------------------------------------------------------------------- 15 | class C[M[List[_]]] // error | ^^^^^^^ | Type parameter List for class C shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:16:11 ----------------------------------------------------- +-- Error: tests/neg/i17613a.scala:16:11 -------------------------------------------------------------------------------- 16 | type E[M[List[_]]] = Int // error | ^^^^^^^ | Type parameter List for type E shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613a.scala:17:14 ----------------------------------------------------- +-- Error: tests/neg/i17613a.scala:17:14 -------------------------------------------------------------------------------- 17 | def foo[N[M[List[_]]]] = ??? // error | ^^^^^^^ | Type parameter List for method foo shadows the type defined by type List in package scala diff --git a/tests/neg/i17613a.scala b/tests/neg/i17613a.scala index 4639bd4b5053..d0e413098cec 100644 --- a/tests/neg/i17613a.scala +++ b/tests/neg/i17613a.scala @@ -1,23 +1,23 @@ -// scalac: -Xlint:type-parameter-shadow +//> using options -Xfatal-warnings -Xlint:type-parameter-shadow object i17613a: class B: type T = Int trait D - + def foobar[D](in: D) = in.toString // error method parameter shadows some other type type MySeq[D] = Seq[D] // error type member's parameter shadows some other type - + class Foo[T](t: T): // error class parameter shadows some other type def bar[T](w: T) = w.toString // error a type parameter shadows another type parameter - + // even deeply nested... class C[M[List[_]]] // error type E[M[List[_]]] = Int // error def foo[N[M[List[_]]]] = ??? // error // ...but not between type parameters in the same list - class F[A, M[N[A]]] + class F[A, M[N[A]]] type G[A, M[L[A]]] = Int def bar[A, N[M[L[A]]]] = ??? def main(args: Array[String]) = println("Test for type parameter shadow") diff --git a/tests/neg/i17613b.check b/tests/neg/i17613b.check index 2bde162ac51e..d8cf8618fb27 100644 --- a/tests/neg/i17613b.check +++ b/tests/neg/i17613b.check @@ -1,56 +1,56 @@ --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:9:13 ---------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:9:13 ------------------------------------------------------------------------- 9 | def foobar[ImTrait](in: D) = in.toString // error | ^^^^^^^ | Type parameter ImTrait for method foobar shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:10:13 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:10:13 ------------------------------------------------------------------------ 10 | type MySeq[ImTrait] = Seq[D] // error | ^^^^^^^ | Type parameter ImTrait for type MySeq shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:12:14 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:12:14 ------------------------------------------------------------------------ 12 | def foobar2[ImClass](in: D) = in.toString // error | ^^^^^^^ | Type parameter ImClass for method foobar2 shadows the type defined by class ImClass in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:13:14 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:13:14 ------------------------------------------------------------------------ 13 | type MySeq2[ImClass] = Seq[D] // error | ^^^^^^^ | Type parameter ImClass for type MySeq2 shadows the type defined by class ImClass in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:16:24 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:16:24 ------------------------------------------------------------------------ 16 | type TypeLambda[A] = [ImTrait] =>> Map[ImTrait, B] // error | ^^^^^^^ | Type parameter ImTrait for type TypeLambda shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:17:21 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:17:21 ------------------------------------------------------------------------ 17 | type PolyFun[A] = [ImTrait] => ImTrait => B // error | ^^^^^^^ | Type parameter ImTrait for type PolyFun shadows the type defined by trait ImTrait in object importTry --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:23:12 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:23:12 ------------------------------------------------------------------------ 23 | class Foo[T](t: T): // error class parameter shadows some other type | ^ | Type parameter T for class Foo shadows the type defined by type T in class B --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:27:15 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:27:15 ------------------------------------------------------------------------ 27 | def intType[List1](x: T) = x.toString() // error | ^^^^^ | Type parameter List1 for method intType shadows an explicitly renamed type : List1 --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:32:10 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:32:10 ------------------------------------------------------------------------ 32 | given [Int]: Ordering[Int]() // error | ^^^ | Type parameter Int for method given_Ordering_Int shadows the type defined by class Int in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:34:12 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:34:12 ------------------------------------------------------------------------ 34 | class C[M[List[_]]] // error List not renamed here | ^^^^^^^ | Type parameter List for class C shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:35:11 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:35:11 ------------------------------------------------------------------------ 35 | type E[M[Int[_]]] = Int // error | ^^^^^^ | Type parameter Int for type E shadows the type defined by class Int in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:37:14 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:37:14 ------------------------------------------------------------------------ 37 | def foo[N[M[List[_]]]] = // error | ^^^^^^^ | Type parameter List for method foo shadows the type defined by type List in package scala --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:40:11 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:40:11 ------------------------------------------------------------------------ 40 | type Z[ImClassR] = Int // error | ^^^^^^^^ | Type parameter ImClassR for type Z shadows an explicitly renamed type : ImClassR --- Error: tests/neg-custom-args/fatal-warnings/i17613b/i17613b.scala:41:18 --------------------------------------------- +-- Error: tests/neg/i17613b/i17613b.scala:41:18 ------------------------------------------------------------------------ 41 | class InnerCl[ImClassR] // error | ^^^^^^^^ | Type parameter ImClassR for class InnerCl shadows an explicitly renamed type : ImClassR diff --git a/tests/neg/i17613b/i17613b.scala b/tests/neg/i17613b/i17613b.scala index 9eeef7c29997..b0c4f11b949c 100644 --- a/tests/neg/i17613b/i17613b.scala +++ b/tests/neg/i17613b/i17613b.scala @@ -1,4 +1,4 @@ -// scalac: -Xlint:type-parameter-shadow +//> using options -Xfatal-warnings -Xlint:type-parameter-shadow object i17613b: import importTry._ @@ -41,4 +41,4 @@ object i17613b: class InnerCl[ImClassR] // error 5 - def main(args: Array[String]) = println("Test for type parameter shadow") + def main(args: Array[String]) = println("Test for type parameter shadow") \ No newline at end of file From 7b1b89810c2b191f40a3cb4c67aee7067b880178 Mon Sep 17 00:00:00 2001 From: Carl Date: Wed, 27 Sep 2023 02:23:46 +0200 Subject: [PATCH 064/134] Apply review changes [Cherry-picked e3c984eb0f1a0574c0a504777073ed391dc39c1d] --- .../tools/dotc/transform/CheckShadowing.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala index 68dce87db388..ae69c1596009 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -127,16 +127,15 @@ class CheckShadowing extends MiniPhase: !owner.isConstructor && !owner.is(Synthetic) && !owner.is(Exported) private def reportShadowing(res: ShadowingData.ShadowResult)(using Context): Unit = - res.warnings.sortBy(w => (w.pos.line, w.pos.startPos.column))(using Ordering[(Int, Int)]).foreach { s => - s match - case PrivateShadowWarning(pos, shadow, shadowed) => - report.warning(s"${shadow.showLocated} shadows field ${shadowed.name} inherited from ${shadowed.owner}", pos) - case TypeParamShadowWarning(pos, shadow, parent, shadowed) => - if shadowed.exists then - report.warning(s"Type parameter ${shadow.name} for $parent shadows the type defined by ${shadowed.showLocated}", pos) - else - report.warning(s"Type parameter ${shadow.name} for $parent shadows an explicitly renamed type : ${shadow.name}", pos) - } + res.warnings.sortBy(w => (w.pos.line, w.pos.startPos.column))(using Ordering[(Int, Int)]).foreach { + case PrivateShadowWarning(pos, shadow, shadowed) => + report.warning(s"${shadow.showLocated} shadows field ${shadowed.name} inherited from ${shadowed.owner}", pos) + case TypeParamShadowWarning(pos, shadow, parent, shadowed) => + if shadowed.exists then + report.warning(s"Type parameter ${shadow.name} for $parent shadows the type defined by ${shadowed.showLocated}", pos) + else + report.warning(s"Type parameter ${shadow.name} for $parent shadows an explicitly renamed type : ${shadow.name}", pos) + } private def nestedTypeTraverser(parent: Symbol) = new TreeTraverser: import tpd._ @@ -311,4 +310,5 @@ object CheckShadowing: /** A container for the results of the shadow elements analysis */ case class ShadowResult(warnings: List[ShadowWarning]) -end CheckShadowing \ No newline at end of file +end CheckShadowing + From ddc505a63894a02fa0acc54e25a0cc5fed375558 Mon Sep 17 00:00:00 2001 From: Carl Date: Wed, 27 Sep 2023 16:07:10 +0200 Subject: [PATCH 065/134] Separate CheckUnused and CheckShadowing into two MegaPhases [Cherry-picked 0f4f7bf328834147976222b40597c31a1ff40c86] --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 ++- .../dotty/tools/dotc/transform/CheckUnused.scala | 13 +------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 1383017b0e2a..126e54a7b49e 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,10 +35,11 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer + List(new CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckShadowing) :: // Check shadowing elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files - List(new CheckUnused.PostTyper, new CheckShadowing) :: // Check for unused elements and shadowing elements List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6e91af4fe2e8..730ef9a32d2e 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -645,17 +645,6 @@ object CheckUnused: imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) ) - /** Returns some inherited symbol with the same type and name as the given "symDecl" */ - private def lookForInheritedDecl(symDecl: Symbol)(using Context): Option[Symbol] = - val symDeclType = symDecl.info - val bClasses = symDecl.owner.info.baseClasses - bClasses match - case _ :: inherited => - inherited - .map(classSymbol => symDecl.denot.matchingDecl(classSymbol, symDeclType)) - .find(sym => sym.name == symDecl.name) - case Nil => - None extension (tree: ImportSelector) @@ -732,7 +721,7 @@ object CheckUnused: /** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ private def isOverriden(using Context): Boolean = - sym.is(Flags.Override) || lookForInheritedDecl(sym).isDefined + sym.is(Flags.Override) || (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) end extension From 070d1bbffbd598c5bd195128652f594fc2a00e83 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Fri, 29 Sep 2023 14:22:09 +0200 Subject: [PATCH 066/134] bugfix: add `moduleClass` imported symbols in `IndexedContext` [Cherry-picked 2531498dc707824e64c8abd30a6aacffde84fa90] --- .../src/main/dotty/tools/pc/IndexedContext.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala b/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala index 01456a367bba..03bc711e8f18 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala @@ -5,6 +5,7 @@ import scala.util.control.NonFatal import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags.* +import dotty.tools.dotc.core.NameOps.moduleClassName import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* @@ -170,7 +171,11 @@ object IndexedContext: initial ++ fromPackageObjects def fromImport(site: Type, name: Name)(using Context): List[Symbol] = - List(site.member(name.toTypeName), site.member(name.toTermName)) + List( + site.member(name.toTypeName), + site.member(name.toTermName), + site.member(name.moduleClassName), + ) .flatMap(_.alternatives) .map(_.symbol) From 507eb90b36e64aabe0823b4264de08aa1b2cba09 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:12:35 +0200 Subject: [PATCH 067/134] Add default arguments to `derivedRefinedType` [Cherry-picked ab9f88219c1901be22243d084843c322cfb079a7][modified] --- compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala | 2 +- .../src/dotty/tools/dotc/core/ConstraintHandling.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 8 +++++--- .../tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Synthesizer.scala | 4 ++-- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index e6287bb3db5d..e989574ee794 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -783,7 +783,7 @@ class CheckCaptures extends Recheck, SymTransformer: adaptTypeFun(actual, rinfo.resType, expected, covariant, insertBox, ares1 => val rinfo1 = rinfo.derivedLambdaType(rinfo.paramNames, rinfo.paramInfos, ares1) - val actual1 = actual.derivedRefinedType(actual.parent, actual.refinedName, rinfo1) + val actual1 = actual.derivedRefinedType(refinedInfo = rinfo1) actual1 ) case _ => diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 0adf632b03e1..37bce69e008b 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -681,7 +681,7 @@ trait ConstraintHandling { case tp: AndType => tp.derivedAndType(tp.tp1.hardenUnions, tp.tp2.hardenUnions) case tp: RefinedType => - tp.derivedRefinedType(tp.parent.hardenUnions, tp.refinedName, tp.refinedInfo) + tp.derivedRefinedType(parent = tp.parent.hardenUnions) case tp: RecType => tp.rebind(tp.parent.hardenUnions) case tp: HKTypeLambda => diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 8dac1f95e1b1..1e86c5039c7d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1731,7 +1731,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private def fixRecs(anchor: SingletonType, tp: Type): Type = { def fix(tp: Type): Type = tp.stripTypeVar match { case tp: RecType => fix(tp.parent).substRecThis(tp, anchor) - case tp @ RefinedType(parent, rname, rinfo) => tp.derivedRefinedType(fix(parent), rname, rinfo) + case tp: RefinedType => tp.derivedRefinedType(parent = fix(tp.parent)) case tp: TypeParamRef => fixOrElse(bounds(tp).hi, tp) case tp: TypeProxy => fixOrElse(tp.superType, tp) case tp: AndType => tp.derivedAndType(fix(tp.tp1), fix(tp.tp2)) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ad60fa460c9f..8b2749fc1254 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1328,7 +1328,7 @@ object Types { case tp: AndType => tp.derivedAndType(tp.tp1.widenUnionWithoutNull, tp.tp2.widenUnionWithoutNull) case tp: RefinedType => - tp.derivedRefinedType(tp.parent.widenUnion, tp.refinedName, tp.refinedInfo) + tp.derivedRefinedType(parent = tp.parent.widenUnion) case tp: RecType => tp.rebind(tp.parent.widenUnion) case tp: HKTypeLambda => @@ -3152,7 +3152,9 @@ object Types { def checkInst(using Context): this.type = this // debug hook - def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(using Context): Type = + final def derivedRefinedType + (parent: Type = this.parent, refinedName: Name = this.refinedName, refinedInfo: Type = this.refinedInfo) + (using Context): Type = if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this else RefinedType(parent, refinedName, refinedInfo) @@ -4056,7 +4058,7 @@ object Types { case tp @ AppliedType(tycon, args) if defn.isFunctionNType(tp) => wrapConvertible(tp.derivedAppliedType(tycon, args.init :+ addInto(args.last))) case tp @ defn.RefinedFunctionOf(rinfo) => - wrapConvertible(tp.derivedRefinedType(tp.parent, tp.refinedName, addInto(rinfo))) + wrapConvertible(tp.derivedRefinedType(refinedInfo = addInto(rinfo))) case tp: MethodOrPoly => tp.derivedLambdaType(resType = addInto(tp.resType)) case ExprType(resType) => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index deb022d3c261..9fb696b23ef7 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -730,8 +730,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val info1 = info.symbol.info assert(info1.derivesFrom(defn.SingletonClass)) RefinedType(parent1, name, info1.mapReduceAnd(removeSingleton)(_ & _)) - case info => - tp.derivedRefinedType(parent1, name, info) + case _ => + tp.derivedRefinedType(parent = parent1) } case tp @ AppliedType(tycon, args) => val tycon1 = tycon.safeDealias diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 061266014615..93acc01d28ad 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1077,7 +1077,7 @@ trait Checking { case tp @ AppliedType(tycon, args) => tp.derivedAppliedType(tycon, args.mapConserve(checkGoodBounds)) case tp: RefinedType => - tp.derivedRefinedType(tp.parent, tp.refinedName, checkGoodBounds(tp.refinedInfo)) + tp.derivedRefinedType(refinedInfo = checkGoodBounds(tp.refinedInfo)) case _ => tp } diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 47d90a4ffcef..080048a9e91e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -537,7 +537,7 @@ object Inferencing { } if tparams.isEmpty then tp else tp.derivedAppliedType(tycon, args1) case tp: AndOrType => tp.derivedAndOrType(captureWildcards(tp.tp1), captureWildcards(tp.tp2)) - case tp: RefinedType => tp.derivedRefinedType(captureWildcards(tp.parent), tp.refinedName, tp.refinedInfo) + case tp: RefinedType => tp.derivedRefinedType(parent = captureWildcards(tp.parent)) case tp: RecType => tp.derivedRecType(captureWildcards(tp.parent)) case tp: LazyRef => captureWildcards(tp.ref) case tp: AnnotatedType => tp.derivedAnnotatedType(captureWildcards(tp.parent), tp.annot) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 1772155e54c7..d1307c794af4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -727,8 +727,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): def recur(handlers: SpecialHandlers): TreeWithErrors = handlers match case (cls, handler) :: rest => def baseWithRefinements(tp: Type): Type = tp.dealias match - case tp @ RefinedType(parent, rname, rinfo) => - tp.derivedRefinedType(baseWithRefinements(parent), rname, rinfo) + case tp: RefinedType => + tp.derivedRefinedType(parent = baseWithRefinements(tp.parent)) case _ => tp.baseType(cls) val base = baseWithRefinements(formal) From e8a8428c22c5dfe666d963ae3891ed9e53252022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Rochala?= <48657087+rochala@users.noreply.github.com> Date: Tue, 3 Oct 2023 16:22:17 +0200 Subject: [PATCH 068/134] Support completions for extension definition parameter (#18331) Extension methods are extended into normal definitions. Because of that typed trees don't include any information about the extension method definition parameter: ```scala extension (x: In@@) ``` In order to add completions, we check if there is an exact path to the untyped tree, and if not, we fall back to it. There may also be more possible cases like that, but I can't think of any at the moment. [Cherry-picked de4ad2b6c0bef31ee4a3750572222e95fcd9a278] --- .../tools/dotc/interactive/Completion.scala | 128 ++++++++---- .../src/dotty/tools/repl/ReplCompiler.scala | 35 +++- .../src/dotty/tools/repl/ReplDriver.scala | 5 +- .../dotty/tools/repl/TabcompleteTests.scala | 5 + .../tools/languageserver/CompletionTest.scala | 71 ++++++- .../tools/pc/completions/Completions.scala | 168 +++++---------- .../pc/printer/ShortenedTypePrinter.scala | 15 +- .../pc/tests/completion/CompletionSuite.scala | 197 ++++++++++++++++-- .../completion/CompletionWorkspaceSuite.scala | 6 +- 9 files changed, 421 insertions(+), 209 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index e4d0cce9f6f9..8fb844f1f333 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -1,8 +1,7 @@ package dotty.tools.dotc.interactive -import scala.language.unsafeNulls - import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.config.Printers.interactiv import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ @@ -25,6 +24,10 @@ import dotty.tools.dotc.util.SourcePosition import scala.collection.mutable import scala.util.control.NonFatal +import dotty.tools.dotc.core.ContextOps.localContext +import dotty.tools.dotc.core.Names +import dotty.tools.dotc.core.Types +import dotty.tools.dotc.core.Symbols /** * One of the results of a completion query. @@ -37,7 +40,7 @@ import scala.util.control.NonFatal */ case class Completion(label: String, description: String, symbols: List[Symbol]) -object Completion { +object Completion: import dotty.tools.dotc.ast.tpd._ @@ -45,10 +48,9 @@ object Completion { * * @return offset and list of symbols for possible completions */ - def completions(pos: SourcePosition)(using Context): (Int, List[Completion]) = { - val path = Interactive.pathTo(ctx.compilationUnit.tpdTree, pos.span) + def completions(pos: SourcePosition)(using Context): (Int, List[Completion]) = + val path: List[Tree] = Interactive.pathTo(ctx.compilationUnit.tpdTree, pos.span) computeCompletions(pos, path)(using Interactive.contextOfPath(path).withPhase(Phases.typerPhase)) - } /** * Inspect `path` to determine what kinds of symbols should be considered. @@ -60,10 +62,11 @@ object Completion { * * Otherwise, provide no completion suggestion. */ - def completionMode(path: List[Tree], pos: SourcePosition): Mode = - path match { - case Ident(_) :: Import(_, _) :: _ => Mode.ImportOrExport - case (ref: RefTree) :: _ => + def completionMode(path: List[untpd.Tree], pos: SourcePosition): Mode = + path match + case untpd.Ident(_) :: untpd.Import(_, _) :: _ => Mode.ImportOrExport + case untpd.Ident(_) :: (_: untpd.ImportSelector) :: _ => Mode.ImportOrExport + case (ref: untpd.RefTree) :: _ => if (ref.name.isTermName) Mode.Term else if (ref.name.isTypeName) Mode.Type else Mode.None @@ -72,9 +75,8 @@ object Completion { if sel.imported.span.contains(pos.span) then Mode.ImportOrExport else Mode.None // Can't help completing the renaming - case (_: ImportOrExport) :: _ => Mode.ImportOrExport + case (_: untpd.ImportOrExport) :: _ => Mode.ImportOrExport case _ => Mode.None - } /** When dealing with in varios palces we check to see if they are * due to incomplete backticks. If so, we ensure we get the full prefix @@ -101,10 +103,13 @@ object Completion { case (sel: untpd.ImportSelector) :: _ => completionPrefix(sel.imported :: Nil, pos) + case untpd.Ident(_) :: (sel: untpd.ImportSelector) :: _ if !sel.isGiven => + completionPrefix(sel.imported :: Nil, pos) + case (tree: untpd.ImportOrExport) :: _ => - tree.selectors.find(_.span.contains(pos.span)).map { selector => + tree.selectors.find(_.span.contains(pos.span)).map: selector => completionPrefix(selector :: Nil, pos) - }.getOrElse("") + .getOrElse("") // Foo.`se will result in Select(Ident(Foo), ) case (select: untpd.Select) :: _ if select.name == nme.ERROR => @@ -118,27 +123,65 @@ object Completion { if (ref.name == nme.ERROR) "" else ref.name.toString.take(pos.span.point - ref.span.point) - case _ => - "" + case _ => "" + end completionPrefix /** Inspect `path` to determine the offset where the completion result should be inserted. */ - def completionOffset(path: List[Tree]): Int = - path match { - case (ref: RefTree) :: _ => ref.span.point + def completionOffset(untpdPath: List[untpd.Tree]): Int = + untpdPath match { + case (ref: untpd.RefTree) :: _ => ref.span.point case _ => 0 } - private def computeCompletions(pos: SourcePosition, path: List[Tree])(using Context): (Int, List[Completion]) = { - val mode = completionMode(path, pos) - val rawPrefix = completionPrefix(path, pos) + /** Some information about the trees is lost after Typer such as Extension method construct + * is expanded into methods. In order to support completions in those cases + * we have to rely on untyped trees and only when types are necessary use typed trees. + */ + def resolveTypedOrUntypedPath(tpdPath: List[Tree], pos: SourcePosition)(using Context): List[untpd.Tree] = + lazy val untpdPath: List[untpd.Tree] = NavigateAST + .pathTo(pos.span, List(ctx.compilationUnit.untpdTree), true).collect: + case untpdTree: untpd.Tree => untpdTree + + tpdPath match + case (_: Bind) :: _ => tpdPath + case (_: untpd.TypTree) :: _ => tpdPath + case _ => untpdPath + + /** Handle case when cursor position is inside extension method construct. + * The extension method construct is then desugared into methods, and consturct parameters + * are no longer a part of a typed tree, but instead are prepended to method parameters. + * + * @param untpdPath The typed or untyped path to the tree that is being completed + * @param tpdPath The typed path that will be returned if no extension method construct is found + * @param pos The cursor position + * + * @return Typed path to the parameter of the extension construct if found or tpdPath + */ + private def typeCheckExtensionConstructPath( + untpdPath: List[untpd.Tree], tpdPath: List[Tree], pos: SourcePosition + )(using Context): List[Tree] = + untpdPath.collectFirst: + case untpd.ExtMethods(paramss, _) => + val enclosingParam = paramss.flatten.find(_.span.contains(pos.span)) + enclosingParam.map: param => + ctx.typer.index(paramss.flatten) + val typedEnclosingParam = ctx.typer.typed(param) + Interactive.pathTo(typedEnclosingParam, pos.span) + .flatten.getOrElse(tpdPath) + + private def computeCompletions(pos: SourcePosition, tpdPath: List[Tree])(using Context): (Int, List[Completion]) = + val path0 = resolveTypedOrUntypedPath(tpdPath, pos) + val mode = completionMode(path0, pos) + val rawPrefix = completionPrefix(path0, pos) val hasBackTick = rawPrefix.headOption.contains('`') val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix val completer = new Completer(mode, prefix, pos) - val completions = path match { + val adjustedPath = typeCheckExtensionConstructPath(path0, tpdPath, pos) + val completions = adjustedPath match // Ignore synthetic select from `This` because in code it was `Ident` // See example in dotty.tools.languageserver.CompletionTest.syntheticThis case Select(qual @ This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions @@ -147,13 +190,12 @@ object Completion { case (tree: ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr) case (_: untpd.ImportSelector) :: Import(expr, _) :: _ => completer.directMemberCompletions(expr) case _ => completer.scopeCompletions - } val describedCompletions = describeCompletions(completions) val backtickedCompletions = describedCompletions.map(completion => backtickCompletions(completion, hasBackTick)) - val offset = completionOffset(path) + val offset = completionOffset(path0) interactiv.println(i"""completion with pos = $pos, | prefix = ${completer.prefix}, @@ -161,7 +203,6 @@ object Completion { | type = ${completer.mode.is(Mode.Type)} | results = $backtickedCompletions%, %""") (offset, backtickedCompletions) - } def backtickCompletions(completion: Completion, hasBackTick: Boolean) = if hasBackTick || needsBacktick(completion.label) then @@ -174,17 +215,17 @@ object Completion { // https://github.com/scalameta/metals/blob/main/mtags/src/main/scala/scala/meta/internal/mtags/KeywordWrapper.scala // https://github.com/com-lihaoyi/Ammonite/blob/73a874173cd337f953a3edc9fb8cb96556638fdd/amm/util/src/main/scala/ammonite/util/Model.scala private def needsBacktick(s: String) = - val chunks = s.split("_", -1) + val chunks = s.split("_", -1).nn val validChunks = chunks.zipWithIndex.forall { case (chunk, index) => - chunk.forall(Chars.isIdentifierPart) || - (chunk.forall(Chars.isOperatorPart) && + chunk.nn.forall(Chars.isIdentifierPart) || + (chunk.nn.forall(Chars.isOperatorPart) && index == chunks.length - 1 && !(chunks.lift(index - 1).contains("") && index - 1 == 0)) } val validStart = - Chars.isIdentifierStart(s(0)) || chunks(0).forall(Chars.isOperatorPart) + Chars.isIdentifierStart(s(0)) || chunks(0).nn.forall(Chars.isOperatorPart) val valid = validChunks && validStart && !keywords.contains(s) @@ -216,7 +257,7 @@ object Completion { * For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map * and they never conflict with each other. */ - class Completer(val mode: Mode, val prefix: String, pos: SourcePosition) { + class Completer(val mode: Mode, val prefix: String, pos: SourcePosition): /** Completions for terms and types that are currently in scope: * the members of the current class, local definitions and the symbols that have been imported, * recursively adding completions from outer scopes. @@ -230,7 +271,7 @@ object Completion { * (even if the import follows it syntactically) * - a more deeply nested import shadowing a member or a local definition causes an ambiguity */ - def scopeCompletions(using context: Context): CompletionMap = { + def scopeCompletions(using context: Context): CompletionMap = val mappings = collection.mutable.Map.empty[Name, List[ScopedDenotations]].withDefaultValue(List.empty) def addMapping(name: Name, denots: ScopedDenotations) = mappings(name) = mappings(name) :+ denots @@ -302,7 +343,7 @@ object Completion { } resultMappings - } + end scopeCompletions /** Widen only those types which are applied or are exactly nothing */ @@ -335,16 +376,16 @@ object Completion { /** Completions introduced by imports directly in this context. * Completions from outer contexts are not included. */ - private def importedCompletions(using Context): CompletionMap = { + private def importedCompletions(using Context): CompletionMap = val imp = ctx.importInfo - def fromImport(name: Name, nameInScope: Name): Seq[(Name, SingleDenotation)] = - imp.site.member(name).alternatives - .collect { case denot if include(denot, nameInScope) => nameInScope -> denot } - if imp == null then Map.empty else + def fromImport(name: Name, nameInScope: Name): Seq[(Name, SingleDenotation)] = + imp.site.member(name).alternatives + .collect { case denot if include(denot, nameInScope) => nameInScope -> denot } + val givenImports = imp.importedImplicits .map { ref => (ref.implicitName: Name, ref.underlyingRef.denot.asSingleDenotation) } .filter((name, denot) => include(denot, name)) @@ -370,7 +411,7 @@ object Completion { }.toSeq.groupByName givenImports ++ wildcardMembers ++ explicitMembers - } + end importedCompletions /** Completions from implicit conversions including old style extensions using implicit classes */ private def implicitConversionMemberCompletions(qual: Tree)(using Context): CompletionMap = @@ -532,7 +573,6 @@ object Completion { extension [N <: Name](namedDenotations: Seq[(N, SingleDenotation)]) @annotation.targetName("groupByNameTupled") def groupByName: CompletionMap = namedDenotations.groupMap((name, denot) => name)((name, denot) => denot) - } private type CompletionMap = Map[Name, Seq[SingleDenotation]] @@ -545,11 +585,11 @@ object Completion { * The completion mode: defines what kinds of symbols should be included in the completion * results. */ - class Mode(val bits: Int) extends AnyVal { + class Mode(val bits: Int) extends AnyVal: def is(other: Mode): Boolean = (bits & other.bits) == other.bits def |(other: Mode): Mode = new Mode(bits | other.bits) - } - object Mode { + + object Mode: /** No symbol should be included */ val None: Mode = new Mode(0) @@ -561,6 +601,4 @@ object Completion { /** Both term and type symbols are allowed */ val ImportOrExport: Mode = new Mode(4) | Term | Type - } -} diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 764695e8479b..d3a5561b6080 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -93,9 +93,9 @@ class ReplCompiler extends Compiler: end compile final def typeOf(expr: String)(using state: State): Result[String] = - typeCheck(expr).map { tree => + typeCheck(expr).map { (_, tpdTree) => given Context = state.context - tree.rhs match { + tpdTree.rhs match { case Block(xs, _) => xs.last.tpe.widen.show case _ => """Couldn't compute the type of your expression, so sorry :( @@ -129,7 +129,7 @@ class ReplCompiler extends Compiler: Iterator(sym) ++ sym.allOverriddenSymbols } - typeCheck(expr).map { + typeCheck(expr).map { (_, tpdTree) => tpdTree match case ValDef(_, _, Block(stats, _)) if stats.nonEmpty => val stat = stats.last.asInstanceOf[tpd.Tree] if (stat.tpe.isError) stat.tpe.show @@ -152,7 +152,7 @@ class ReplCompiler extends Compiler: } } - final def typeCheck(expr: String, errorsAllowed: Boolean = false)(using state: State): Result[tpd.ValDef] = { + final def typeCheck(expr: String, errorsAllowed: Boolean = false)(using state: State): Result[(untpd.ValDef, tpd.ValDef)] = { def wrapped(expr: String, sourceFile: SourceFile, state: State)(using Context): Result[untpd.PackageDef] = { def wrap(trees: List[untpd.Tree]): untpd.PackageDef = { @@ -181,22 +181,32 @@ class ReplCompiler extends Compiler: } } - def unwrapped(tree: tpd.Tree, sourceFile: SourceFile)(using Context): Result[tpd.ValDef] = { - def error: Result[tpd.ValDef] = - List(new Diagnostic.Error(s"Invalid scala expression", - sourceFile.atSpan(Span(0, sourceFile.content.length)))).errors + def error[Tree <: untpd.Tree](sourceFile: SourceFile): Result[Tree] = + List(new Diagnostic.Error(s"Invalid scala expression", + sourceFile.atSpan(Span(0, sourceFile.content.length)))).errors + def unwrappedTypeTree(tree: tpd.Tree, sourceFile0: SourceFile)(using Context): Result[tpd.ValDef] = { import tpd._ tree match { case PackageDef(_, List(TypeDef(_, tmpl: Template))) => tmpl.body .collectFirst { case dd: ValDef if dd.name.show == "expr" => dd.result } - .getOrElse(error) + .getOrElse(error[tpd.ValDef](sourceFile0)) case _ => - error + error[tpd.ValDef](sourceFile0) } } + def unwrappedUntypedTree(tree: untpd.Tree, sourceFile0: SourceFile)(using Context): Result[untpd.ValDef] = + import untpd._ + tree match { + case PackageDef(_, List(TypeDef(_, tmpl: Template))) => + tmpl.body + .collectFirst { case dd: ValDef if dd.name.show == "expr" => dd.result } + .getOrElse(error[untpd.ValDef](sourceFile0)) + case _ => + error[untpd.ValDef](sourceFile0) + } val src = SourceFile.virtual("", expr) inContext(state.context.fresh @@ -209,7 +219,10 @@ class ReplCompiler extends Compiler: ctx.run.nn.compileUnits(unit :: Nil, ctx) if (errorsAllowed || !ctx.reporter.hasErrors) - unwrapped(unit.tpdTree, src) + for + tpdTree <- unwrappedTypeTree(unit.tpdTree, src) + untpdTree <- unwrappedUntypedTree(unit.untpdTree, src) + yield untpdTree -> tpdTree else ctx.reporter.removeBufferedMessages.errors } diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 905f4f06de08..2471f6bece42 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -251,10 +251,11 @@ class ReplDriver(settings: Array[String], given state: State = newRun(state0) compiler .typeCheck(expr, errorsAllowed = true) - .map { tree => + .map { (untpdTree, tpdTree) => val file = SourceFile.virtual("", expr, maybeIncomplete = true) val unit = CompilationUnit(file)(using state.context) - unit.tpdTree = tree + unit.untpdTree = untpdTree + unit.tpdTree = tpdTree given Context = state.context.fresh.setCompilationUnit(unit) val srcPos = SourcePosition(file, Span(cursor)) val completions = try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 910584a9b5e7..0bce525e1469 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -32,6 +32,11 @@ class TabcompleteTests extends ReplTest { assertEquals(List("apply"), comp) } + @Test def tabCompleteInExtensionDefinition = initially { + val comp = tabComplete("extension (x: Lis") + assertEquals(List("List"), comp) + } + @Test def tabCompleteTwiceIn = { val src1 = "class Foo { def bar(xs: List[Int]) = xs.map" val src2 = "class Foo { def bar(xs: List[Int]) = xs.mapC" diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 4dd9c276b8b4..7a21fe15fbe5 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -1523,9 +1523,72 @@ class CompletionTest { |object Test: | def foo: ArrayBuffer[Fo${m1}] = ??? """ + .completion(m1, Set(("Foo",Class,"Foo"))) + } + + @Test def extensionDefinitionCompletions: Unit = + code"""|trait Foo + |object T: + | extension (x: Fo$m1) + |""" + .completion(m1, Set(("Foo",Class,"Foo"))) + + @Test def extensionDefinitionCompletionsSelect: Unit = + code"""|object Test: + | class TestSelect() + |object T: + | extension (x: Test.TestSel$m1) + |""" .completion(m1, Set( - ("Foo",Class,"Foo") - ) - ) - } + ("TestSelect", Module, "Test.TestSelect"), ("TestSelect", Class, "Test.TestSelect") + )) + + @Test def extensionDefinitionCompletionsSelectNested: Unit = + code"""|object Test: + | object Test2: + | class TestSelect() + |object T: + | extension (x: Test.Test2.TestSel$m1) + |""" + .completion(m1, Set( + ("TestSelect", Module, "Test.Test2.TestSelect"), ("TestSelect", Class, "Test.Test2.TestSelect") + )) + + @Test def extensionDefinitionCompletionsSelectInside: Unit = + code"""|object Test: + | object Test2: + | class TestSelect() + |object T: + | extension (x: Test.Te$m1.TestSelect) + |""" + .completion(m1, Set(("Test2", Module, "Test.Test2"))) + + @Test def extensionDefinitionCompletionsTypeParam: Unit = + code"""|object T: + | extension [TypeParam](x: TypePar$m1) + |""" + .completion(m1, Set(("TypeParam", Field, "T.TypeParam"))) + + + @Test def typeParamCompletions: Unit = + code"""|object T: + | def xxx[TTT](x: TT$m1) + |""" + .completion(m1, Set(("TTT", Field, "T.TTT"))) + + @Test def selectDynamic: Unit = + code"""|import scala.language.dynamics + |class Foo extends Dynamic { + | def banana: Int = 42 + | def selectDynamic(field: String): Foo = this + | def applyDynamicNamed(name: String)(arg: (String, Int)): Foo = this + | def updateDynamic(name: String)(value: Int): Foo = this + |} + |object Test: + | val x = new Foo() + | x.sele$m1 + | x.bana$m2 + |""" + .completion(m1, Set(("selectDynamic", Method, "(field: String): Foo"))) + .completion(m2, Set(("banana", Method, "=> Int"))) } diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index 0574aa2cdb7d..64bbbb848289 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -12,6 +12,7 @@ import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering} import scala.meta.pc.* import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.core.Comments.Comment import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.* @@ -24,6 +25,7 @@ import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.interactive.Completion +import dotty.tools.dotc.interactive.Completion.Mode import dotty.tools.dotc.transform.SymUtils.* import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans @@ -54,6 +56,13 @@ class Completions( val coursierComplete = new CoursierComplete(BuildInfo.scalaVersion) + private lazy val completionMode = + val adjustedPath = Completion.resolveTypedOrUntypedPath(path, pos) + val mode = Completion.completionMode(adjustedPath, pos) + path match + case Literal(Constant(_: String)) :: _ => Mode.Term // literal completions + case _ => mode + private lazy val shouldAddSnippet = path match /* In case of `method@@()` we should not add snippets and the path @@ -69,113 +78,38 @@ class Completions( case (_: Ident) :: (_: SeqLiteral) :: _ => false case _ => true - enum CursorPos: - case Type(hasTypeParams: Boolean, hasNewKw: Boolean) - case Term - case Import - - def include(sym: Symbol)(using Context): Boolean = - def hasSyntheticCursorSuffix: Boolean = - if !sym.name.endsWith(Cursor.value) then false - else - val realNameLength = sym.decodedName.length - Cursor.value.length - sym.source == pos.source && - sym.span.start + realNameLength == pos.span.end - - val generalExclude = - isUninterestingSymbol(sym) || - !isNotLocalForwardReference(sym) || - sym.isPackageObject || - hasSyntheticCursorSuffix - - def isWildcardParam(sym: Symbol) = - if sym.isTerm && sym.owner.isAnonymousFunction then - sym.name match - case DerivedName(under, _) => - under.isEmpty - case _ => false - else false + private lazy val allowTemplateSuffix: Boolean = + path match + case _ :: New(selectOrIdent: (Select | Ident)) :: _ => true + case _ => false - if generalExclude then false + def includeSymbol(sym: Symbol)(using Context): Boolean = + def hasSyntheticCursorSuffix: Boolean = + if !sym.name.endsWith(Cursor.value) then false else - this match - case Type(_, _) => true - case Term if isWildcardParam(sym) => false - case Term if sym.isTerm || sym.is(Package) => true - case Import => true + val realNameLength = sym.decodedName.length - Cursor.value.length + sym.source == pos.source && + sym.span.start + realNameLength == pos.span.end + + val generalExclude = + isUninterestingSymbol(sym) || + !isNotLocalForwardReference(sym) || + sym.isPackageObject || + hasSyntheticCursorSuffix + + def isWildcardParam(sym: Symbol) = + if sym.isTerm && sym.owner.isAnonymousFunction then + sym.name match + case DerivedName(under, _) => + under.isEmpty case _ => false - end if - end include + else false - def allowBracketSuffix: Boolean = - this match - case Type(hasTypeParams, _) => !hasTypeParams - case _ => false - - def allowTemplateSuffix: Boolean = - this match - case Type(_, hasNewKw) => hasNewKw - case _ => false - - def allowApplicationSuffix: Boolean = - this match - case Term => true - case _ => false - - end CursorPos - - private lazy val cursorPos = - calculateTypeInstanceAndNewPositions(path) - - private def calculateTypeInstanceAndNewPositions( - path: List[Tree] - ): CursorPos = - path match - case (_: Import) :: _ => CursorPos.Import - case _ :: (_: Import) :: _ => CursorPos.Import - case (head: (Select | Ident)) :: tail => - // https://github.com/lampepfl/dotty/issues/15750 - // due to this issue in dotty, because of which trees after typer lose information, - // we have to calculate hasNoSquareBracket manually: - val hasSquareBracket = - val span: Span = head.srcPos.span - if span.exists then - var i = span.end - while i < (text.length() - 1) && text(i).isWhitespace do i = i + 1 - - if i < text.length() then text(i) == '[' - else false - else false - - def typePos = CursorPos.Type(hasSquareBracket, hasNewKw = false) - def newTypePos = - CursorPos.Type(hasSquareBracket, hasNewKw = true) - - tail match - case (v: ValOrDefDef) :: _ if v.tpt.sourcePos.contains(pos) => - typePos - case New(selectOrIdent: (Select | Ident)) :: _ - if selectOrIdent.sourcePos.contains(pos) => - newTypePos - case (a @ AppliedTypeTree(_, args)) :: _ - if args.exists(_.sourcePos.contains(pos)) => - typePos - case (templ @ Template(constr, _, self, _)) :: _ - if (constr :: self :: templ.parents).exists( - _.sourcePos.contains(pos) - ) => - typePos - case _ => - CursorPos.Term - end match - - case (_: TypeTree) :: TypeApply(Select(newQualifier: New, _), _) :: _ - if newQualifier.sourcePos.contains(pos) => - CursorPos.Type(hasTypeParams = false, hasNewKw = true) - - case _ => CursorPos.Term - end match - end calculateTypeInstanceAndNewPositions + if generalExclude then false + else if completionMode.is(Mode.Type) then true + else !isWildcardParam(sym) && (sym.isTerm || sym.is(Package)) + end if + end includeSymbol def completions(): (List[CompletionValue], SymbolSearch.Result) = val (advanced, exclusive) = advancedCompletions(path, pos, completionPos) @@ -206,7 +140,7 @@ class Completions( end match val application = CompletionApplication.fromPath(path) - val ordering = completionOrdering(application, cursorPos) + val ordering = completionOrdering(application) val values = application.postProcess(all.sorted(ordering)) (values, result) end completions @@ -256,8 +190,7 @@ class Completions( private def findSuffix(symbol: Symbol): CompletionSuffix = CompletionSuffix.empty .chain { suffix => // for [] suffix - if shouldAddSnippet && - cursorPos.allowBracketSuffix && symbol.info.typeParams.nonEmpty + if shouldAddSnippet && symbol.info.typeParams.nonEmpty then suffix.withNewSuffixSnippet(SuffixKind.Bracket) else suffix } @@ -285,7 +218,7 @@ class Completions( else suffix } .chain { suffix => // for {} suffix - if shouldAddSnippet && cursorPos.allowTemplateSuffix + if shouldAddSnippet && allowTemplateSuffix && isAbstractType(symbol) then if suffix.hasSnippet then suffix.withNewSuffix(SuffixKind.Template) @@ -305,9 +238,8 @@ class Completions( def companionSynthetic = sym.companion.exists && sym.companion.is(Synthetic) // find the apply completion that would need a snippet val methodSymbols = - if shouldAddSnippet && - (sym.is(Flags.Module) || sym.isClass && !sym.is(Flags.Trait)) && - !sym.is(Flags.JavaDefined) && cursorPos.allowApplicationSuffix + if shouldAddSnippet && completionMode.is(Mode.Term) && + (sym.is(Flags.Module) || sym.isClass && !sym.is(Flags.Trait)) && !sym.is(Flags.JavaDefined) then val info = /* Companion will be added even for normal classes now, @@ -635,7 +567,7 @@ class Completions( val suffix = if symOnly.snippetSuffix.addLabelSnippet then "[]" else "" val id = nameId + suffix - val include = cursorPos.include(sym) + val include = includeSymbol(sym) (id, include) case kw: CompletionValue.Keyword => (kw.label, true) case mc: CompletionValue.MatchCompletion => (mc.label, true) @@ -695,7 +627,6 @@ class Completions( private def computeRelevancePenalty( completion: CompletionValue, application: CompletionApplication, - cursorPos: CursorPos, ): Int = import scala.meta.internal.pc.MemberOrdering.* @@ -741,10 +672,8 @@ class Completions( relevance |= IsSynthetic if sym.isDeprecated then relevance |= IsDeprecated if isEvilMethod(sym.name) then relevance |= IsEvilMethod - cursorPos match - case CursorPos.Type(_, _) if !sym.isType => - relevance |= IsNotTypeInTypePos - case _ => + if !completionMode.is(Mode.ImportOrExport) && + completionMode.is(Mode.Type) && !sym.isType then relevance |= IsNotTypeInTypePos relevance end symbolRelevance @@ -822,8 +751,7 @@ class Completions( end CompletionApplication private def completionOrdering( - application: CompletionApplication, - cursorPos: CursorPos, + application: CompletionApplication ): Ordering[CompletionValue] = new Ordering[CompletionValue]: val queryLower = completionPos.query.toLowerCase() @@ -838,8 +766,8 @@ class Completions( def compareByRelevance(o1: CompletionValue, o2: CompletionValue): Int = Integer.compare( - computeRelevancePenalty(o1, application, cursorPos), - computeRelevancePenalty(o2, application, cursorPos), + computeRelevancePenalty(o1, application), + computeRelevancePenalty(o2, application), ) def fuzzyScore(o: CompletionValue.Symbolic): Int = diff --git a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala index 088ecd6c3a0c..5652fd0d9bcc 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala @@ -234,18 +234,25 @@ class ShortenedTypePrinter( end match end hoverSymbol + def isImportedByDefault(sym: Symbol): Boolean = + import dotty.tools.dotc.core.Symbols.defn + lazy val effectiveOwner = sym.effectiveOwner + sym.isType && (effectiveOwner == defn.ScalaPackageClass || effectiveOwner == defn.ScalaPredefModuleClass) + def completionSymbol(sym: Symbol): String = val info = sym.info.widenTermRefExpr val typeSymbol = info.typeSymbol - if sym.is(Flags.Package) || sym.isClass then " " + fullNameString(sym.effectiveOwner) - else if sym.is(Flags.Module) || typeSymbol.is(Flags.Module) then + lazy val typeEffectiveOwner = if typeSymbol != NoSymbol then " " + fullNameString(typeSymbol.effectiveOwner) else " " + fullNameString(sym.effectiveOwner) + + if isImportedByDefault(sym) then typeEffectiveOwner + else if sym.is(Flags.Package) || sym.isClass then " " + fullNameString(sym.effectiveOwner) + else if sym.is(Flags.Module) || typeSymbol.is(Flags.Module) then typeEffectiveOwner else if sym.is(Flags.Method) then defaultMethodSignature(sym, info, onlyMethodParams = true) - else if sym.isType - then + else if sym.isType then info match case TypeAlias(t) => " = " + tpe(t.resultType) case t => tpe(t.resultType) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 313013c34de1..bcd47259bb57 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -539,7 +539,7 @@ class CompletionSuite extends BaseCompletionSuite: | new Foo().bana@@ |} |""".stripMargin, - "selectDynamic(field: String): Foo" + "banana: Int" ) @Test def dynamic2 = @@ -549,7 +549,7 @@ class CompletionSuite extends BaseCompletionSuite: | val x = new Foo().foo.bana@@ |} |""".stripMargin, - "selectDynamic(field: String): Foo" + "banana: Int" ) @Test def dynamic3 = @@ -560,7 +560,7 @@ class CompletionSuite extends BaseCompletionSuite: | (foo.bar = 42).bana@@ |} |""".stripMargin, - "selectDynamic(field: String): Foo" + "banana: Int" ) @Test def dynamic4 = @@ -570,7 +570,7 @@ class CompletionSuite extends BaseCompletionSuite: | val foo = new Foo().foo(x = 42).bana@@ |} |""".stripMargin, - "selectDynamic(field: String): Foo" + "banana: Int" ) @Test def dynamic5 = @@ -669,14 +669,12 @@ class CompletionSuite extends BaseCompletionSuite: check( s"""|object Main { | Option(1) match { - | case _: S@@ + | case _: Som@@ |} |""".stripMargin, """|Some[?] scala - |Seq scala.collection.immutable - |Set scala.collection.immutable |""".stripMargin, - topLines = Some(3) + topLines = Some(1) ) @Test def adt3 = @@ -695,9 +693,8 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """|NotString: Int |Number: Regex - |Nil scala.collection.immutable |""".stripMargin, - topLines = Option(3) + topLines = Some(2) ) @Test def adt4 = @@ -705,29 +702,24 @@ class CompletionSuite extends BaseCompletionSuite: s"""|object Main { | val Number = "".r | "" match { - | case _: N@@ + | case _: Numb@@ |} |""".stripMargin, """|Number: Regex - |Nil scala.collection.immutable - |NoManifest scala.reflect |""".stripMargin, - topLines = Option(3) + topLines = Some(1) ) - @Test def adt5 = + @Test def `no-methods-on-case-type` = check( s"""|object Main { | val Number = "".r | "" match { - | case _: N@@ + | case _: NotImpl@@ |} |""".stripMargin, - """|Number: Regex - |Nil scala.collection.immutable - |NoManifest scala.reflect + """|NotImplementedError scala |""".stripMargin, - topLines = Option(3) ) @Test def underscore = @@ -1326,6 +1318,171 @@ class CompletionSuite extends BaseCompletionSuite: """|AClass[A <: Int] test.O |AClass test.O |AbstractTypeClassManifest - scala.reflect.ClassManifestFactory + """.stripMargin + ) + + @Test def `extension-definition-scope` = + check( + """|trait Foo + |object T: + | extension (x: Fo@@) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-symbol-search` = + check( + """|object T: + | extension (x: ListBuffe@@) + |""".stripMargin, + """|ListBuffer[A] - scala.collection.mutable + |ListBuffer - scala.collection.mutable + |""".stripMargin, + ) + + @Test def `extension-definition-type-parameter` = + check( + """|trait Foo + |object T: + | extension [A <: Fo@@] + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-type-parameter-symbol-search` = + check( + """|object T: + | extension [A <: ListBuffe@@] + |""".stripMargin, + """|ListBuffer[A] - scala.collection.mutable + |ListBuffer - scala.collection.mutable + |""".stripMargin + ) + + @Test def `extension-definition-using-param-clause` = + check( + """|trait Foo + |object T: + | extension (using Fo@@) + |""".stripMargin, + """|Foo test |""".stripMargin ) + + @Test def `extension-definition-mix-1` = + check( + """|trait Foo + |object T: + | extension (x: Int)(using Fo@@) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-mix-2` = + check( + """|trait Foo + |object T: + | extension (using Fo@@)(x: Int)(using Foo) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-mix-3` = + check( + """|trait Foo + |object T: + | extension (using Foo)(x: Int)(using Fo@@) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-mix-4` = + check( + """|trait Foo + |object T: + | extension [A](x: Fo@@) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-mix-5` = + check( + """|trait Foo + |object T: + | extension [A](using Fo@@)(x: Int) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-mix-6` = + check( + """|trait Foo + |object T: + | extension [A](using Foo)(x: Fo@@) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-mix-7` = + check( + """|trait Foo + |object T: + | extension [A](using Foo)(x: Fo@@)(using Foo) + |""".stripMargin, + """|Foo test + |""".stripMargin + ) + + @Test def `extension-definition-select` = + check( + """|object Test: + | class TestSelect() + |object T: + | extension (x: Test.TestSel@@) + |""".stripMargin, + """|TestSelect test.Test + |""".stripMargin + ) + + @Test def `extension-definition-select-mix-1` = + check( + """|object Test: + | class TestSelect() + |object T: + | extension (using Int)(x: Test.TestSel@@) + |""".stripMargin, + """|TestSelect test.Test + |""".stripMargin + ) + + @Test def `extension-definition-select-mix-2` = + check( + """|object Test: + | class TestSelect[T]() + |object T: + | extension [T](x: Test.TestSel@@) + |""".stripMargin, + """|TestSelect[T] test.Test + |TestSelect test.Test + |""".stripMargin + ) + + @Test def `no-square-brackets` = + checkEdit( + """|object O: + | val a = List.appl@@ + |""".stripMargin, + """|object O: + | val a = List.apply($0) + |""".stripMargin, + ) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala index 500b083137e1..c51b66bd5f2e 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala @@ -293,7 +293,6 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: |""".stripMargin ) - // Ignore for Scala 3, since we don't provide completions for null @Test def `match-typed` = checkEdit( """|object Main { @@ -305,11 +304,12 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: """|import java.util.ArrayDeque |object Main { | def foo(): Unit = null match { - | case x: ArrayDeque => + | case x: ArrayDeque[$0] => | } |} |""".stripMargin, - filter = _.contains("java.util") + filter = _.contains("java.util"), + assertSingleItem = false, ) @Test def `type` = From e1460f231820180e5eade5faac6835881bb980ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Rochala?= <48657087+rochala@users.noreply.github.com> Date: Tue, 3 Oct 2023 19:15:18 +0200 Subject: [PATCH 069/134] Make PC tests more stable (#18640) [Cherry-picked c14d2de520b3a17bf3c6a3636482274f27d1853e] --- .../test/dotty/tools/pc/tests/completion/CompletionSuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index bcd47259bb57..7ca7f9a19b04 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -715,7 +715,7 @@ class CompletionSuite extends BaseCompletionSuite: s"""|object Main { | val Number = "".r | "" match { - | case _: NotImpl@@ + | case _: NotImplementedErr@@ |} |""".stripMargin, """|NotImplementedError scala From f92118b316fcca3e6db1f063f452c5ac10e9382d Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 29 Sep 2023 12:25:50 +0200 Subject: [PATCH 070/134] Fix false positive in WUnused for renamed path-dependent imports (attempt 2) [Cherry-picked cde97d8adbef89111bd20c760edeb5cc2617b5c8] --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 6 +++--- tests/pos/i18366.scala | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i18366.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 730ef9a32d2e..3b397d8be746 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -337,7 +337,7 @@ object CheckUnused: * - usage */ private class UnusedData: - import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} + import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack, ListBuffer => MutList} import UnusedData.* /** The current scope during the tree traversal */ @@ -346,7 +346,7 @@ object CheckUnused: var unusedAggregate: Option[UnusedResult] = None /* IMPORTS */ - private val impInScope = MutStack(MutSet[tpd.Import]()) + private val impInScope = MutStack(MutList[tpd.Import]()) /** * We store the symbol along with their accessibility without import. * Accessibility to their definition in outer context/scope @@ -449,7 +449,7 @@ object CheckUnused: def pushScope(newScopeType: ScopeType): Unit = // unused imports : currScopeType.push(newScopeType) - impInScope.push(MutSet()) + impInScope.push(MutList()) usedInScope.push(MutSet()) def registerSetVar(sym: Symbol): Unit = diff --git a/tests/pos/i18366.scala b/tests/pos/i18366.scala new file mode 100644 index 000000000000..698510ad13a2 --- /dev/null +++ b/tests/pos/i18366.scala @@ -0,0 +1,10 @@ +//> using options -Xfatal-warnings -Wunused:all + +trait Builder { + def foo(): Unit +} + +def repro = + val builder: Builder = ??? + import builder.{foo => bar} + bar() \ No newline at end of file From c5c4e486bc594bdd8407337efa8bb3f8577ae26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 29 Sep 2023 10:16:12 +0200 Subject: [PATCH 071/134] BCode: Track the exact types on the stack, rather than only the height. [Cherry-picked a82248f50ca1fca33a27fa5f5d306075e4012bad] --- .../tools/backend/jvm/BCodeBodyBuilder.scala | 113 ++++++++++-------- .../tools/backend/jvm/BCodeSkelBuilder.scala | 56 ++++++++- 2 files changed, 115 insertions(+), 54 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index e7b5a0dad1bf..974f26f7adeb 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -79,14 +79,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { tree match { case Assign(lhs @ DesugaredSelect(qual, _), rhs) => - val savedStackHeight = stackHeight + val savedStackSize = stack.recordSize() val isStatic = lhs.symbol.isStaticMember if (!isStatic) { - genLoadQualifier(lhs) - stackHeight += 1 + val qualTK = genLoad(qual) + stack.push(qualTK) } genLoad(rhs, symInfoTK(lhs.symbol)) - stackHeight = savedStackHeight + stack.restoreSize(savedStackSize) lineNumber(tree) // receiverClass is used in the bytecode to access the field. using sym.owner may lead to IllegalAccessError val receiverClass = qual.tpe.typeSymbol @@ -150,9 +150,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } genLoad(larg, resKind) - stackHeight += resKind.size + stack.push(resKind) genLoad(rarg, if (isShift) INT else resKind) - stackHeight -= resKind.size + stack.pop() (code: @switch) match { case ADD => bc add resKind @@ -189,19 +189,19 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { if (isArrayGet(code)) { // load argument on stack assert(args.length == 1, s"Too many arguments for array get operation: $tree"); - stackHeight += 1 + stack.push(k) genLoad(args.head, INT) - stackHeight -= 1 + stack.pop() generatedType = k.asArrayBType.componentType bc.aload(elementType) } else if (isArraySet(code)) { val List(a1, a2) = args - stackHeight += 1 + stack.push(k) genLoad(a1, INT) - stackHeight += 1 + stack.push(INT) genLoad(a2) - stackHeight -= 2 + stack.pop(2) generatedType = UNIT bc.astore(elementType) } else { @@ -235,7 +235,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val resKind = if (hasUnitBranch) UNIT else tpeTK(tree) val postIf = new asm.Label - genLoadTo(thenp, resKind, LoadDestination.Jump(postIf, stackHeight)) + genLoadTo(thenp, resKind, LoadDestination.Jump(postIf, stack.recordSize())) markProgramPoint(failure) genLoadTo(elsep, resKind, LoadDestination.FallThrough) markProgramPoint(postIf) @@ -294,8 +294,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { ) } - def genLoad(tree: Tree): Unit = { - genLoad(tree, tpeTK(tree)) + def genLoad(tree: Tree): BType = { + val generatedType = tpeTK(tree) + genLoad(tree, generatedType) + generatedType } /* Generate code for trees that produce values on the stack */ @@ -364,6 +366,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case t @ Ident(_) => (t, Nil) } + val savedStackSize = stack.recordSize() if (!fun.symbol.isStaticMember) { // load receiver of non-static implementation of lambda @@ -372,10 +375,12 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // AbstractValidatingLambdaMetafactory.validateMetafactoryArgs val DesugaredSelect(prefix, _) = fun: @unchecked - genLoad(prefix) + val prefixTK = genLoad(prefix) + stack.push(prefixTK) } genLoadArguments(env, fun.symbol.info.firstParamTypes map toTypeKind) + stack.restoreSize(savedStackSize) generatedType = genInvokeDynamicLambda(NoSymbol, fun.symbol, env.size, functionalInterface) case app @ Apply(_, _) => @@ -494,9 +499,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { dest match case LoadDestination.FallThrough => () - case LoadDestination.Jump(label, targetStackHeight) => - if targetStackHeight < stackHeight then - val stackDiff = stackHeight - targetStackHeight + case LoadDestination.Jump(label, targetStackSize) => + val stackDiff = stack.heightDiffWrt(targetStackSize) + if stackDiff != 0 then if expectedType == UNIT then bc dropMany stackDiff else @@ -599,7 +604,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { if dest == LoadDestination.FallThrough then val resKind = tpeTK(tree) val jumpTarget = new asm.Label - registerJumpDest(labelSym, resKind, LoadDestination.Jump(jumpTarget, stackHeight)) + registerJumpDest(labelSym, resKind, LoadDestination.Jump(jumpTarget, stack.recordSize())) genLoad(expr, resKind) markProgramPoint(jumpTarget) resKind @@ -657,7 +662,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { markProgramPoint(loop) if isInfinite then - val dest = LoadDestination.Jump(loop, stackHeight) + val dest = LoadDestination.Jump(loop, stack.recordSize()) genLoadTo(body, UNIT, dest) dest else @@ -672,7 +677,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val failure = new asm.Label genCond(cond, success, failure, targetIfNoJump = success) markProgramPoint(success) - genLoadTo(body, UNIT, LoadDestination.Jump(loop, stackHeight)) + genLoadTo(body, UNIT, LoadDestination.Jump(loop, stack.recordSize())) markProgramPoint(failure) end match LoadDestination.FallThrough @@ -765,10 +770,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // on the stack (contrary to what the type in the AST says). // scala/bug#10290: qual can be `this.$outer()` (not just `this`), so we call genLoad (not just ALOAD_0) - genLoad(superQual) - stackHeight += 1 + val superQualTK = genLoad(superQual) + stack.push(superQualTK) genLoadArguments(args, paramTKs(app)) - stackHeight -= 1 + stack.pop() generatedType = genCallMethod(fun.symbol, InvokeStyle.Super, app.span) // 'new' constructor call: Note: since constructors are @@ -790,9 +795,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { assert(classBTypeFromSymbol(ctor.owner) == rt, s"Symbol ${ctor.owner.showFullName} is different from $rt") mnode.visitTypeInsn(asm.Opcodes.NEW, rt.internalName) bc dup generatedType - stackHeight += 2 + stack.push(rt) + stack.push(rt) genLoadArguments(args, paramTKs(app)) - stackHeight -= 2 + stack.pop(2) genCallMethod(ctor, InvokeStyle.Special, app.span) case _ => @@ -825,12 +831,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { else if (app.hasAttachment(BCodeHelpers.UseInvokeSpecial)) InvokeStyle.Special else InvokeStyle.Virtual - val savedStackHeight = stackHeight + val savedStackSize = stack.recordSize() if invokeStyle.hasInstance then - genLoadQualifier(fun) - stackHeight += 1 + stack.push(genLoadQualifier(fun)) genLoadArguments(args, paramTKs(app)) - stackHeight = savedStackHeight + stack.restoreSize(savedStackSize) val DesugaredSelect(qual, name) = fun: @unchecked // fun is a Select, also checked in genLoadQualifier val isArrayClone = name == nme.clone_ && qual.tpe.widen.isInstanceOf[JavaArrayType] @@ -888,7 +893,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { bc iconst elems.length bc newarray elmKind - stackHeight += 3 // during the genLoad below, there is the result, its dup, and the index + // during the genLoad below, there is the result, its dup, and the index + stack.push(generatedType) + stack.push(generatedType) + stack.push(INT) var i = 0 var rest = elems @@ -901,7 +909,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { i = i + 1 } - stackHeight -= 3 + stack.pop(3) generatedType } @@ -917,7 +925,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val (generatedType, postMatch, postMatchDest) = if dest == LoadDestination.FallThrough then val postMatch = new asm.Label - (tpeTK(tree), postMatch, LoadDestination.Jump(postMatch, stackHeight)) + (tpeTK(tree), postMatch, LoadDestination.Jump(postMatch, stack.recordSize())) else (expectedType, null, dest) @@ -1179,7 +1187,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } /* Emit code to Load the qualifier of `tree` on top of the stack. */ - def genLoadQualifier(tree: Tree): Unit = { + def genLoadQualifier(tree: Tree): BType = { lineNumber(tree) tree match { case DesugaredSelect(qualifier, _) => genLoad(qualifier) @@ -1188,6 +1196,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case Some(sel) => genLoadQualifier(sel) case None => assert(t.symbol.owner == this.claszSymbol) + UNIT } case _ => abort(s"Unknown qualifier $tree") } @@ -1200,14 +1209,14 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { btpes match case btpe :: btpes1 => genLoad(arg, btpe) - stackHeight += btpe.size + stack.push(btpe) loop(args1, btpes1) case _ => case _ => - val savedStackHeight = stackHeight + val savedStackSize = stack.recordSize() loop(args, btpes) - stackHeight = savedStackHeight + stack.restoreSize(savedStackSize) end genLoadArguments def genLoadModule(tree: Tree): BType = { @@ -1307,13 +1316,13 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { }.sum bc.genNewStringBuilder(approxBuilderSize) - stackHeight += 1 // during the genLoad below, there is a reference to the StringBuilder on the stack + stack.push(jlStringBuilderRef) // during the genLoad below, there is a reference to the StringBuilder on the stack for (elem <- concatArguments) { val elemType = tpeTK(elem) genLoad(elem, elemType) bc.genStringBuilderAppend(elemType) } - stackHeight -= 1 + stack.pop() bc.genStringBuilderEnd } else { @@ -1331,7 +1340,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { var totalArgSlots = 0 var countConcats = 1 // ie. 1 + how many times we spilled - val savedStackHeight = stackHeight + val savedStackSize = stack.recordSize() for (elem <- concatArguments) { val tpe = tpeTK(elem) @@ -1339,7 +1348,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // Unlikely spill case if (totalArgSlots + elemSlots >= MaxIndySlots) { - stackHeight = savedStackHeight + countConcats + stack.restoreSize(savedStackSize) + for _ <- 0 until countConcats do + stack.push(StringRef) bc.genIndyStringConcat(recipe.toString, argTypes.result(), constVals.result()) countConcats += 1 totalArgSlots = 0 @@ -1364,10 +1375,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val tpe = tpeTK(elem) argTypes += tpe.toASMType genLoad(elem, tpe) - stackHeight += 1 + stack.push(tpe) } } - stackHeight = savedStackHeight + stack.restoreSize(savedStackSize) bc.genIndyStringConcat(recipe.toString, argTypes.result(), constVals.result()) // If we spilled, generate one final concat @@ -1562,9 +1573,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } else { val tk = tpeTK(l).maxType(tpeTK(r)) genLoad(l, tk) - stackHeight += tk.size + stack.push(tk) genLoad(r, tk) - stackHeight -= tk.size + stack.pop() genCJUMP(success, failure, op, tk, targetIfNoJump) } } @@ -1679,9 +1690,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } genLoad(l, ObjectRef) - stackHeight += 1 + stack.push(ObjectRef) genLoad(r, ObjectRef) - stackHeight -= 1 + stack.pop() genCallMethod(equalsMethod, InvokeStyle.Static) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } @@ -1697,9 +1708,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } else if (isNonNullExpr(l)) { // SI-7852 Avoid null check if L is statically non-null. genLoad(l, ObjectRef) - stackHeight += 1 + stack.push(ObjectRef) genLoad(r, ObjectRef) - stackHeight -= 1 + stack.pop() genCallMethod(defn.Any_equals, InvokeStyle.Virtual) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } else { @@ -1709,9 +1720,9 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val lNonNull = new asm.Label genLoad(l, ObjectRef) - stackHeight += 1 + stack.push(ObjectRef) genLoad(r, ObjectRef) - stackHeight -= 1 + stack.pop() locals.store(eqEqTempLocal) bc dup ObjectRef genCZJUMP(lNull, lNonNull, Primitives.EQ, ObjectRef, targetIfNoJump = lNull) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index f892c2bb753c..49ff83aa716a 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -40,12 +40,62 @@ trait BCodeSkelBuilder extends BCodeHelpers { lazy val NativeAttr: Symbol = requiredClass[scala.native] + final class BTypesStack: + // Anecdotally, growing past 16 to 32 is common; growing past 32 is rare + private var stack = new Array[BType](32) + private var size = 0 + + def push(btype: BType): Unit = + if size == stack.length then + stack = java.util.Arrays.copyOf(stack, stack.length * 2) + stack(size) = btype + size += 1 + + def pop(): Unit = pop(1) + + def pop(count: Int): Unit = + assert(size >= count) + size -= count + + def height: Int = heightBetween(0, size) + + private def heightBetween(start: Int, end: Int): Int = + var result = 0 + var i = start + while i != end do + result += stack(i).size + i += 1 + result + + def recordSize(): BTypesStack.Size = BTypesStack.intToSize(size) + + def restoreSize(targetSize: BTypesStack.Size): Unit = + val targetSize1 = BTypesStack.sizeToInt(targetSize) + assert(size >= targetSize1) + size = targetSize1 + + def heightDiffWrt(targetSize: BTypesStack.Size): Int = + val targetSize1 = BTypesStack.sizeToInt(targetSize) + assert(size >= targetSize1) + heightBetween(targetSize1, size) + + def clear(): Unit = + size = 0 + end BTypesStack + + object BTypesStack: + opaque type Size = Int + + private def intToSize(size: Int): Size = size + private def sizeToInt(size: Size): Int = size + end BTypesStack + /** The destination of a value generated by `genLoadTo`. */ enum LoadDestination: /** The value is put on the stack, and control flows through to the next opcode. */ case FallThrough /** The value is put on the stack, and control flow is transferred to the given `label`. */ - case Jump(label: asm.Label, targetStackHeight: Int) + case Jump(label: asm.Label, targetStackSize: BTypesStack.Size) /** The value is RETURN'ed from the enclosing method. */ case Return /** The value is ATHROW'n. */ @@ -369,7 +419,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { var earlyReturnVar: Symbol = null var shouldEmitCleanup = false // stack tracking - var stackHeight = 0 + val stack = new BTypesStack // line numbers var lastEmittedLineNr = -1 @@ -589,7 +639,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { earlyReturnVar = null shouldEmitCleanup = false - stackHeight = 0 + stack.clear() lastEmittedLineNr = -1 } From 700a8da6516aa7cea57ba450e32e5bd3e1b9af87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 29 Sep 2023 13:25:37 +0200 Subject: [PATCH 072/134] Opt: Get rid of the LiftTry phase; instead handle things in the back-end. When we enter a `try-catch` at the JVM level, we have to make sure that the stack is empty. That's because, upon exception, the JVM wipes the stack, and we must not lose operands that are already on the stack that we will still use. Previously, this was achieved with a transformation phase, `LiftTry`, which lifted problematic `try-catch`es in local `def`s, called `liftedTree$x`. It analyzed the tree to predict which `try-catch`es would execute on a non-empty stack when eventually compiled to the JVM. This approach has several shortcomings. It exhibits performance cliffs, as the generated def can then cause more variables to be boxed in to `XRef`s. These were the only extra defs created for implementation reasons rather than for language reasons. As a user of the language, it is hard to predict when such a lifted def will be needed. The additional `liftedTree` methods also show up on stack traces and obfuscate them. Debugging can be severely hampered as well. Phases executing after `LiftTry`, notably `CapturedVars`, also had to take care not to create more problematic situations as a result of their transformations, which is hard to predict and to remember. Finally, Scala.js and Scala Native do not have the same restriction, so they received suboptimal code for no reason. In this commit, we entirely remove the `LiftTry` phase. Instead, we enhance the JVM back-end to deal with the situation. When starting a `try-catch` on a non-empty stack, we stash the entire contents of the stack into local variables. After the `try-catch`, we pop all those local variables back onto the stack. We also null out the leftover vars not to prevent garbage collection. This new approach solves all of the problems mentioned above. [Cherry-picked 52e8e74c3b7e92cc0244145714ac2ce97d78f7cf] --- .../tools/backend/jvm/BCodeSkelBuilder.scala | 12 +++ .../tools/backend/jvm/BCodeSyncAndTry.scala | 58 +++++++++++- compiler/src/dotty/tools/dotc/Compiler.scala | 1 - .../src/dotty/tools/dotc/core/NameKinds.scala | 1 - .../tools/dotc/transform/CapturedVars.scala | 38 +------- .../dotty/tools/dotc/transform/LiftTry.scala | 88 ------------------- docs/_docs/internals/overall-structure.md | 1 - tests/neg/t1672b.scala | 1 - tests/run/i1692.scala | 2 +- tests/run/i1692b.scala | 2 +- tests/run/i4866.check | 2 - tests/run/i4866.scala | 21 ----- 12 files changed, 75 insertions(+), 152 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/transform/LiftTry.scala delete mode 100644 tests/run/i4866.check delete mode 100644 tests/run/i4866.scala diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 49ff83aa716a..70d4d758d83b 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -45,6 +45,8 @@ trait BCodeSkelBuilder extends BCodeHelpers { private var stack = new Array[BType](32) private var size = 0 + def isEmpty: Boolean = size == 0 + def push(btype: BType): Unit = if size == stack.length then stack = java.util.Arrays.copyOf(stack, stack.length * 2) @@ -81,6 +83,16 @@ trait BCodeSkelBuilder extends BCodeHelpers { def clear(): Unit = size = 0 + + def acquireFullStack(): IArray[BType] = + val res = IArray.unsafeFromArray(stack.slice(0, size)) + size = 0 + res + + def restoreFullStack(fullStack: IArray[BType]): Unit = + assert(size == 0 && stack.length >= fullStack.length) + fullStack.copyToArray(stack) + size = fullStack.length end BTypesStack object BTypesStack: diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala index b5ed27511e7e..74e1c5812b14 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSyncAndTry.scala @@ -118,6 +118,11 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { /* * Emitting try-catch is easy, emitting try-catch-finally not quite so. + * + * For a try-catch, the only thing we need to care about is to stash the stack away + * in local variables and load them back in afterwards, in case the incoming stack + * is not empty. + * * A finally-block (which always has type Unit, thus leaving the operand stack unchanged) * affects control-transfer from protected regions, as follows: * @@ -190,7 +195,7 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { } } - // ------ (0) locals used later ------ + // ------ locals used later ------ /* * `postHandlers` is a program point denoting: @@ -203,6 +208,13 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { */ val postHandlers = new asm.Label + // stack stash + val needStackStash = !stack.isEmpty && !caseHandlers.isEmpty + val acquiredStack = if needStackStash then stack.acquireFullStack() else null + val stashLocals = + if acquiredStack == null then null + else acquiredStack.uncheckedNN.filter(_ != UNIT).map(btpe => locals.makeTempLocal(btpe)) + val hasFinally = (finalizer != tpd.EmptyTree) /* @@ -222,6 +234,17 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { */ val finCleanup = if (hasFinally) new asm.Label else null + /* ------ (0) Stash the stack into local variables, if necessary. + * From top of the stack down to the bottom. + * ------ + */ + + if stashLocals != null then + val stashLocalsNN = stashLocals.uncheckedNN // why is this necessary? + for i <- (stashLocalsNN.length - 1) to 0 by -1 do + val local = stashLocalsNN(i) + bc.store(local.idx, local.tk) + /* ------ (1) try-block, protected by: * (1.a) the EHs due to case-clauses, emitted in (2), * (1.b) the EH due to finally-clause, emitted in (3.A) @@ -367,6 +390,39 @@ trait BCodeSyncAndTry extends BCodeBodyBuilder { emitFinalizer(finalizer, tmp, isDuplicate = false) // the only invocation of emitFinalizer with `isDuplicate == false` } + /* ------ (5) Unstash the stack, if it was stashed before. + * From bottom of the stack to the top. + * If there is a non-UNIT result, we need to temporarily store + * that one in a local variable while we unstash. + * ------ + */ + + if stashLocals != null then + val stashLocalsNN = stashLocals.uncheckedNN // why is this necessary? + + val resultLoc = + if kind == UNIT then null + else if tmp != null then locals(tmp) // reuse the same local + else locals.makeTempLocal(kind) + if resultLoc != null then + bc.store(resultLoc.idx, kind) + + for i <- 0 until stashLocalsNN.size do + val local = stashLocalsNN(i) + bc.load(local.idx, local.tk) + if local.tk.isRef then + bc.emit(asm.Opcodes.ACONST_NULL) + bc.store(local.idx, local.tk) + + stack.restoreFullStack(acquiredStack.nn) + + if resultLoc != null then + bc.load(resultLoc.idx, kind) + if kind.isRef then + bc.emit(asm.Opcodes.ACONST_NULL) + bc.store(resultLoc.idx, kind) + end if // stashLocals != null + kind } // end of genLoadTry() diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 126e54a7b49e..d34fa247b155 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -101,7 +101,6 @@ class Compiler { new Getters, // Replace non-private vals and vars with getter defs (fields are added later) new SpecializeFunctions, // Specialized Function{0,1,2} by replacing super with specialized super new SpecializeTuples, // Specializes Tuples by replacing tuple construction and selection trees - new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods new CollectNullableFields, // Collect fields that can be nulled out after use in lazy initialization new ElimOuterSelect, // Expand outer selections new ResolveSuper, // Implement super accessors diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 2b60e52ae13a..a844f5f17689 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -314,7 +314,6 @@ object NameKinds { val TailTempName: UniqueNameKind = new UniqueNameKind("$tmp") val ExceptionBinderName: UniqueNameKind = new UniqueNameKind("ex") val SkolemName: UniqueNameKind = new UniqueNameKind("?") - val LiftedTreeName: UniqueNameKind = new UniqueNameKind("liftedTree") val SuperArgName: UniqueNameKind = new UniqueNameKind("$superArg$") val DocArtifactName: UniqueNameKind = new UniqueNameKind("$doc") val UniqueInlineName: UniqueNameKind = new UniqueNameKind("$i") diff --git a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala index 32bcc53184b1..8964beb26051 100644 --- a/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/compiler/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -25,9 +25,6 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer: override def description: String = CapturedVars.description - override def runsAfterGroupsOf: Set[String] = Set(LiftTry.name) - // lifting tries changes what variables are considered to be captured - private[this] var Captured: Store.Location[util.ReadOnlySet[Symbol]] = _ private def captured(using Context) = ctx.store(Captured) @@ -131,42 +128,15 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer: * * intRef.elem = expr * - * rewrite using a temporary var to - * - * val ev$n = expr - * intRef.elem = ev$n - * - * That way, we avoid the problem that `expr` might contain a `try` that would - * run on a non-empty stack (which is illegal under JVM rules). Note that LiftTry - * has already run before, so such `try`s would not be eliminated. - * - * If the ref type lhs is followed by a cast (can be an artifact of nested translation), - * drop the cast. - * - * If the ref type is `ObjectRef` or `VolatileObjectRef`, immediately assign `null` - * to the temporary to make the underlying target of the reference available for - * garbage collection. Nullification is omitted if the `expr` is already `null`. - * - * var ev$n: RHS = expr - * objRef.elem = ev$n - * ev$n = null.asInstanceOf[RHS] + * the lhs can be followed by a cast as an artifact of nested translation. + * In that case, drop the cast. */ override def transformAssign(tree: Assign)(using Context): Tree = - def absolved: Boolean = tree.rhs match - case Literal(Constant(null)) | Typed(Literal(Constant(null)), _) => true - case _ => false - def recur(lhs: Tree): Tree = lhs match + tree.lhs match case TypeApply(Select(qual@Select(_, nme.elem), nme.asInstanceOf_), _) => - recur(qual) - case Select(_, nme.elem) if refInfo.boxedRefClasses.contains(lhs.symbol.maybeOwner) => - val tempDef = transformFollowing(SyntheticValDef(TempResultName.fresh(), tree.rhs, flags = Mutable)) - val update = cpy.Assign(tree)(lhs, ref(tempDef.symbol)) - def reset = cpy.Assign(tree)(ref(tempDef.symbol), nullLiteral.cast(tempDef.symbol.info)) - val res = if refInfo.objectRefClasses(lhs.symbol.maybeOwner) && !absolved then reset else unitLiteral - transformFollowing(Block(tempDef :: update :: Nil, res)) + cpy.Assign(tree)(qual, tree.rhs) case _ => tree - recur(tree.lhs) object CapturedVars: val name: String = "capturedVars" diff --git a/compiler/src/dotty/tools/dotc/transform/LiftTry.scala b/compiler/src/dotty/tools/dotc/transform/LiftTry.scala deleted file mode 100644 index 6acb1013d509..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/LiftTry.scala +++ /dev/null @@ -1,88 +0,0 @@ -package dotty.tools.dotc -package transform - -import MegaPhase._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.NameKinds.LiftedTreeName -import NonLocalReturns._ -import util.Store - -/** Lifts try's that might be executed on non-empty expression stacks - * to their own methods. I.e. - * - * try body catch handler - * - * is lifted to - * - * { def liftedTree$n() = try body catch handler; liftedTree$n() } - * - * However, don't lift try's without catch expressions (try-finally). - * Lifting is needed only for try-catch expressions that are evaluated in a context - * where the stack might not be empty. `finally` does not attempt to continue evaluation - * after an exception, so the fact that values on the stack are 'lost' does not matter - * (copied from https://github.com/scala/scala/pull/922). - */ -class LiftTry extends MiniPhase with IdentityDenotTransformer { thisPhase => - import ast.tpd._ - - override def phaseName: String = LiftTry.name - - override def description: String = LiftTry.description - - private var NeedLift: Store.Location[Boolean] = _ - private def needLift(using Context): Boolean = ctx.store(NeedLift) - - override def initContext(ctx: FreshContext): Unit = - NeedLift = ctx.addLocation(false) - - private def liftingCtx(p: Boolean)(using Context) = - if (needLift == p) ctx else ctx.fresh.updateStore(NeedLift, p) - - override def prepareForApply(tree: Apply)(using Context): Context = - liftingCtx(true) - - override def prepareForDefDef(tree: DefDef)(using Context): Context = - liftingCtx(false) - - override def prepareForValDef(tree: ValDef)(using Context): Context = - if !tree.symbol.exists - || tree.symbol.isSelfSym - || tree.symbol.owner == ctx.owner.enclosingMethod - && !tree.symbol.is(Lazy) - // The current implementation wraps initializers of lazy vals in - // calls to an initialize method, which means that a `try` in the - // initializer needs to be lifted. Note that the new scheme proposed - // in #6979 would avoid this. - then ctx - else liftingCtx(true) - - override def prepareForAssign(tree: Assign)(using Context): Context = - if (tree.lhs.symbol.maybeOwner == ctx.owner.enclosingMethod) ctx - else liftingCtx(true) - - override def prepareForReturn(tree: Return)(using Context): Context = - if (!isNonLocalReturn(tree)) ctx - else liftingCtx(true) - - override def prepareForTemplate(tree: Template)(using Context): Context = - liftingCtx(false) - - override def transformTry(tree: Try)(using Context): Tree = - if (needLift && tree.cases.nonEmpty) { - report.debuglog(i"lifting tree at ${tree.span}, current owner = ${ctx.owner}") - val fn = newSymbol( - ctx.owner, LiftedTreeName.fresh(), Synthetic | Method, - MethodType(Nil, tree.tpe.widenIfUnstable), coord = tree.span) - tree.changeOwnerAfter(ctx.owner, fn, thisPhase) - Block(DefDef(fn, tree) :: Nil, ref(fn).appliedToNone) - } - else tree -} -object LiftTry: - val name = "liftTry" - val description: String = "lift any try that might be executed on a non-empty expression stack" diff --git a/docs/_docs/internals/overall-structure.md b/docs/_docs/internals/overall-structure.md index 5bb43eb946a8..ab936ddd8512 100644 --- a/docs/_docs/internals/overall-structure.md +++ b/docs/_docs/internals/overall-structure.md @@ -151,7 +151,6 @@ phases. The current list of phases is specified in class [Compiler] as follows: new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods new Getters, // Replace non-private vals and vars with getter defs (fields are added later) new SpecializeFunctions, // Specialized Function{0,1,2} by replacing super with specialized super - new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods new CollectNullableFields, // Collect fields that can be nulled out after use in lazy initialization new ElimOuterSelect, // Expand outer selections new ResolveSuper, // Implement super accessors diff --git a/tests/neg/t1672b.scala b/tests/neg/t1672b.scala index 84ebb6155e6d..b67664615048 100644 --- a/tests/neg/t1672b.scala +++ b/tests/neg/t1672b.scala @@ -41,7 +41,6 @@ object Test1772B { } } - // the `liftedTree` local method will prevent a tail call here. @tailrec def bar(i : Int) : Int = { if (i == 0) 0 diff --git a/tests/run/i1692.scala b/tests/run/i1692.scala index 09459a073d6e..9416e6101efd 100644 --- a/tests/run/i1692.scala +++ b/tests/run/i1692.scala @@ -22,7 +22,7 @@ class LazyNullable(a: => Int) { lazy val l4Inf = eInf private[this] val i = "I" - // null out i even though the try ends up lifted, because the LazyVals phase runs before the LiftTry phase + // null out i even though the try needs stack stashing lazy val l5 = try i catch { case e: Exception => () } } diff --git a/tests/run/i1692b.scala b/tests/run/i1692b.scala index bd2108038ef4..8f23a0e5a3b3 100644 --- a/tests/run/i1692b.scala +++ b/tests/run/i1692b.scala @@ -24,7 +24,7 @@ class LazyNullable(a: => Int) { @threadUnsafe lazy val l4Inf = try eInf finally () // null out e, since private[this] is inferred private[this] val i = "I" - // null out i even though the try ends up lifted, because the LazyVals phase runs before the LiftTry phase + // null out i even though the try needs stack stashing @threadUnsafe lazy val l5 = try i catch { case e: Exception => () } } diff --git a/tests/run/i4866.check b/tests/run/i4866.check deleted file mode 100644 index f16e2a9c94df..000000000000 --- a/tests/run/i4866.check +++ /dev/null @@ -1,2 +0,0 @@ -Foo #lifted: 0 -FooLifted #lifted: 1 diff --git a/tests/run/i4866.scala b/tests/run/i4866.scala deleted file mode 100644 index 092770fb21cd..000000000000 --- a/tests/run/i4866.scala +++ /dev/null @@ -1,21 +0,0 @@ -// scalajs: --skip - -// Test that try-finally aren't lifted, but try-catch are. - -class Foo { - val field = try 1 finally () -} - -class FooLifted { - val field = try 1 catch { case e: Exception => () } finally () -} - -object Test extends App { - def numLifted(o: Object) = { - def isLifted(m: java.lang.reflect.Method) = m.getName.startsWith("lifted") - o.getClass.getDeclaredMethods.count(isLifted) - } - - println("Foo #lifted: " + numLifted(new Foo)) - println("FooLifted #lifted: " + numLifted(new FooLifted)) -} From f52174cae169e355b2ea7d9d605702092dda9dc9 Mon Sep 17 00:00:00 2001 From: danecek Date: Wed, 4 Oct 2023 16:11:30 +0200 Subject: [PATCH 073/134] Update syntax.md [Cherry-picked ffc702d6d30723b27c122a9d6f696c0b409c1424] --- docs/_docs/reference/syntax.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index a705c5a3fd79..67ebfbe5d3c2 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -391,10 +391,10 @@ Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec | SimpleRef ‘as’ id ImportSpec ::= NamedSelector - | WildcardSelector + | WildCardSelector | ‘{’ ImportSelectors) ‘}’ NamedSelector ::= id [‘as’ (id | ‘_’)] -WildCardSelector ::= ‘*' | ‘given’ [InfixType] +WildCardSelector ::= ‘*’ | ‘given’ [InfixType] ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] | WildCardSelector {‘,’ WildCardSelector} From c9c25bcfcc3f04215a9cd1fd707b3fd0d877ed66 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 7 Sep 2023 18:38:17 +0100 Subject: [PATCH 074/134] Use the unwidened type when casting structural calls So if the call is to a stable val, the call will have a stable type. [Cherry-picked fd97de5d677c41a2a68e560dc0dd32d4cbb235b7] --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 4 ++-- tests/pos/i18263.orig.scala | 16 ++++++++++++++++ tests/pos/i18263.scala | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i18263.orig.scala create mode 100644 tests/pos/i18263.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 717966923708..1c829567058a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -235,14 +235,14 @@ trait Dynamic { if ValueClasses.isDerivedValueClass(tpe.classSymbol) && qual.tpe <:< defn.ReflectSelectableTypeRef then val genericUnderlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass) val underlying = tpe.select(genericUnderlying).widen.resultType - New(tpe, tree.cast(underlying) :: Nil) + New(tpe.widen, tree.cast(underlying) :: Nil) else tree maybeBoxed.cast(tpe) fun.tpe.widen match { case tpe: ValueType => - structuralCall(nme.selectDynamic, Nil).maybeBoxingCast(tpe) + structuralCall(nme.selectDynamic, Nil).maybeBoxingCast(fun.tpe) case tpe: MethodType => def isDependentMethod(tpe: Type): Boolean = tpe match { diff --git a/tests/pos/i18263.orig.scala b/tests/pos/i18263.orig.scala new file mode 100644 index 000000000000..68b000580f08 --- /dev/null +++ b/tests/pos/i18263.orig.scala @@ -0,0 +1,16 @@ +sealed trait Scope +sealed trait Domain extends Scope +object Domain extends Domain + +trait Baz[T] +def baz(using ck: Scope): Baz[ck.type] = ??? + +class Foo extends scala.reflect.Selectable: + type TScope = Domain + final protected given TScope = Domain + +object ID: + val internal1 = new Foo: + val ii = new Foo: + val x = baz + val z = internal1.ii.x //error diff --git a/tests/pos/i18263.scala b/tests/pos/i18263.scala new file mode 100644 index 000000000000..4fe79999afe7 --- /dev/null +++ b/tests/pos/i18263.scala @@ -0,0 +1,15 @@ +final class Bar +final class Inv[T] +class Foo extends scala.reflect.Selectable: + type Boo = Bar + final given boo1: Boo = new Bar + +class Test: + def mkInv(using bar: Bar): Inv[bar.type] = new Inv() + + def test: Unit = + val foo1 /* : Foo { val foo2: { z1 => Foo { val inv1: Inv[(z1.boo1 : z1.Boo)] }}} */ = new Foo: + val foo2 /* : { z1 => Foo { val inv1: Inv[(z1.boo1 : z1.Boo)] }} */ = new Foo: + val inv1 /* : Inv[( boo1 : Boo)] */ = mkInv /* (this.boo1) */ + val inv2 = foo1.foo2.inv1 // error + () From 5fa63e880e162376b4b0b9af53d022c49224a484 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 8 Sep 2023 13:36:16 +0100 Subject: [PATCH 075/134] Add a .widenExpr call [Cherry-picked f1758a61d46d8cd8a474fc50ca3b5c918967ac9a] --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 7 +------ compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 5ac1c23d2e7f..befac83b824b 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -962,16 +962,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => !tree.symbol.exists && tree.isTerm && hasRefinement(tree.qualifier.tpe) - def loop(tree: Tree): Boolean = tree match - case TypeApply(fun, _) => - loop(fun) - case Apply(fun, _) => - loop(fun) + funPart(tree) match case tree: Select => isStructuralTermSelect(tree) case _ => false - loop(tree) } /** Return a pair consisting of (supercall, rest) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 1c829567058a..51734e1a5d4b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -242,7 +242,7 @@ trait Dynamic { fun.tpe.widen match { case tpe: ValueType => - structuralCall(nme.selectDynamic, Nil).maybeBoxingCast(fun.tpe) + structuralCall(nme.selectDynamic, Nil).maybeBoxingCast(fun.tpe.widenExpr) case tpe: MethodType => def isDependentMethod(tpe: Type): Boolean = tpe match { From d638138f7481b06d40dc898cc44ed52c5f49559d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 26 Sep 2023 10:25:16 +0100 Subject: [PATCH 076/134] Fix HoverProvider [Cherry-picked af81e64cb81efd295f02b4b071492fb8e28e0186] --- .../src/main/dotty/tools/pc/HoverProvider.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index 6c2251988f65..1ddb79fabc98 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -181,7 +181,7 @@ object HoverProvider: findRefinement(parent) case _ => None - val refTpe = sel.tpe.metalsDealias match + val refTpe = sel.tpe.widen.metalsDealias match case r: RefinedType => Some(r) case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.metalsDealias) case _ => None From 93a17b1e946e8a44781e64fcdf74c3cbd99086d6 Mon Sep 17 00:00:00 2001 From: Jakub Ciesluk <323892@uwr.edu.pl> Date: Mon, 2 Oct 2023 14:41:15 +0200 Subject: [PATCH 077/134] bugfix: Named args completions with default values [Cherry-picked 6f43fee7173023adf9f0675439fbffd2daeeb18d] --- .../pc/completions/NamedArgCompletions.scala | 17 ++- .../tests/completion/CompletionArgSuite.scala | 105 ++++++++++++++++++ 2 files changed, 118 insertions(+), 4 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala index 30346690bd18..d5ecf60dc341 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala @@ -237,15 +237,24 @@ object NamedArgCompletions: .getOrElse(baseArgs) .filterNot(isUselessLiteral) + @tailrec + def isDefaultArg(t: Tree): Boolean = t match + // default args + case Ident(name) => name.is(DefaultGetterName) + // default args for methods defined in object + case Select(_, name) => + name.is(DefaultGetterName) + // default args in not-first parameter list + // eg. def m(fst: Int)(snd: Int)(arg1: Int, arg2: Int = 123) = ??? + case Apply(fun, _) => isDefaultArg(fun) + case _ => false + val isNamed: Set[Name] = args.iterator .zip(baseParams.iterator) // filter out synthesized args and default arg getters .filterNot { case (arg, _) if arg.symbol.denot.is(Flags.Synthetic) => true - case (Ident(name), _) => name.is(DefaultGetterName) // default args - case (Select(Ident(_), name), _) => - name.is(DefaultGetterName) // default args for apply method - case _ => false + case (arg, _) => isDefaultArg(arg) } .map { case (NamedArg(name, _), _) => name diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala index 6e91209d5788..bce430afe881 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala @@ -238,6 +238,111 @@ class CompletionArgSuite extends BaseCompletionSuite: "" ) + @Test def `default-args` = + check( + s"""|object Main { + | def foo() = { + | def deployment( + | fst: Option[String], + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(@@) + | } + |} + |""".stripMargin, + """|fst = : Option[String] + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + @Test def `default-args2` = + check( + s"""|object Main { + | def deployment( + | fst: Option[String], + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(@@) + |} + |""".stripMargin, + """|fst = : Option[String] + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + @Test def `default-args3` = + check( + s"""|object Main { + | def deployment(str: String)( + | fst: Option[String], + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment("str")( + | @@ + | ) + |} + |""".stripMargin, + """|fst = : Option[String] + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + @Test def `default-args4` = + check( + s"""|object Main { + | def deployment(str: String)(opt: Option[Int])( + | fst: Option[String], + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment("str")(None)( + | @@ + | ) + |} + |""".stripMargin, + """|fst = : Option[String] + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + @Test def `default-args5` = + check( + s"""|object Main { + | def deployment(str: String)(opt: Option[Int] = None)( + | fst: Option[String], + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment("str")( + | @@ + | ) + |} + |""".stripMargin, + """|opt = : Option[Int] + |""".stripMargin, + topLines = Some(1), + ) + + @Test def `default-args6` = + check( + s"""|object Main { + | def deployment(using str: String)( + | fst: Option[String], + | snd: Int = 1, + | ): Option[Int] = ??? + | val abc = deployment(using "str")( + | @@ + | ) + |} + |""".stripMargin, + """|fst = : Option[String] + |snd = : Int + |""".stripMargin, + topLines = Some(2), + ) + + // @Test def `explicit-dollar` = // checkSnippet( // see: https://github.com/scalameta/metals/issues/2400 // """ From e3b70299b547f019ec2b3a6d638a978b49349f64 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:14:40 +0200 Subject: [PATCH 078/134] bugfix: Fix coursier completions tests for the presentation compiler [Cherry-picked 3a545dd49ee9b63f0f69ad12f437fdb83bc5bc46][modified] --- .../tools/pc/tests/completion/CompletionScalaCliSuite.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionScalaCliSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionScalaCliSuite.scala index 1391635bea93..0d86922d4e70 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionScalaCliSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionScalaCliSuite.scala @@ -12,8 +12,7 @@ class CompletionScalaCliSuite extends BaseCompletionSuite: |package A |""".stripMargin, """|io.circe - |io.circul - |""".stripMargin + |io.circul""".stripMargin ) @Test def `multiple-deps` = @@ -131,8 +130,7 @@ class CompletionScalaCliSuite extends BaseCompletionSuite: |package A |""".stripMargin, """|io.circe - |io.circul - |""".stripMargin + |io.circul""".stripMargin ) @Test def `multiple-deps2` = From 9026ffcf24d1778f7bab68cbe77bebea51491115 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Mon, 9 Oct 2023 11:28:41 +0200 Subject: [PATCH 079/134] fix: match completions for type aliases [Cherry-picked f973d0ba64dd774646a17ae3156f59a3e4e268dc] --- .../pc/completions/MatchCaseCompletions.scala | 8 ++- .../completion/CompletionCaseSuite.scala | 41 +++++++++++ .../completion/CompletionMatchSuite.scala | 69 +++++++++++++++++++ 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala index 0949d050a59c..8e7fa821d95d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -90,7 +90,7 @@ object CaseKeywordCompletion: new Parents(NoType, definitions) case sel => new Parents(sel.tpe, definitions) - val selectorSym = parents.selector.typeSymbol + val selectorSym = parents.selector.widen.metalsDealias.typeSymbol // Special handle case when selector is a tuple or `FunctionN`. if definitions.isTupleClass(selectorSym) || definitions.isFunctionClass( @@ -153,7 +153,9 @@ object CaseKeywordCompletion: if isValid(ts) then visit(autoImportsGen.inferSymbolImport(ts)) ) // Step 2: walk through known subclasses of sealed types. - val sealedDescs = subclassesForType(parents.selector.widen.bounds.hi) + val sealedDescs = subclassesForType( + parents.selector.widen.metalsDealias.bounds.hi + ) sealedDescs.foreach { sym => val symbolImport = autoImportsGen.inferSymbolImport(sym) visit(symbolImport) @@ -241,7 +243,7 @@ object CaseKeywordCompletion: completionPos, clientSupportsSnippets ) - val tpe = selector.tpe.widen.bounds.hi match + val tpe = selector.tpe.widen.metalsDealias.bounds.hi match case tr @ TypeRef(_, _) => tr.underlying case t => t diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala index 80a9e843e746..37df23972813 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala @@ -657,3 +657,44 @@ class CompletionCaseSuite extends BaseCompletionSuite: |case Singing(song) => test.Activity |case Sports(time, intensity) => test.Activity""".stripMargin ) + + @Test def `type-alias-case` = + check( + s"""|object O: + | type Id[A] = A + | + | enum Animal: + | case Cat, Dog + | + | val animal: Id[Animal] = ??? + | + | animal match + | cas@@ + |""".stripMargin, + """|case Animal.Cat => + |case Animal.Dog => + |""".stripMargin, + ) + + @Test def `type-alias-sealed-trait-case` = + check( + s"""|object O { + | type Id[A] = A + | + |sealed trait Animal + |object Animal { + | case class Cat() extends Animal + | case object Dog extends Animal + |} + | + | val animal: Id[Animal] = ??? + | + | animal match { + | cas@@ + | } + |} + |""".stripMargin, + """|case Cat() => test.O.Animal + |case Dog => test.O.Animal + |""".stripMargin, + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionMatchSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionMatchSuite.scala index 264e6f56c586..142667171c75 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionMatchSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionMatchSuite.scala @@ -601,3 +601,72 @@ class CompletionMatchSuite extends BaseCompletionSuite: |}""".stripMargin, filter = _.contains("exhaustive") ) + + @Test def `type-alias` = + checkEdit( + s"""|object O { + | type Id[A] = A + | + | enum Animal: + | case Cat, Dog + | + | val animal: Id[Animal] = ??? + | + | animal ma@@ + |} + |""".stripMargin, + s"""object O { + | type Id[A] = A + | + | enum Animal: + | case Cat, Dog + | + | val animal: Id[Animal] = ??? + | + | animal match + |\tcase Animal.Cat => $$0 + |\tcase Animal.Dog => + | + |} + |""".stripMargin, + filter = _.contains("exhaustive"), + ) + + @Test def `type-alias-sealed-trait` = + checkEdit( + s"""|object O { + | type Id[A] = A + | + |sealed trait Animal + |object Animal { + | case object Cat extends Animal + | case object Dog extends Animal + |} + | + | val animal: Id[Animal] = ??? + | + |animal ma@@ + |} + |""".stripMargin, + s"""| + |import O.Animal.Cat + |import O.Animal.Dog + |object O { + | type Id[A] = A + | + |sealed trait Animal + |object Animal { + | case object Cat extends Animal + | case object Dog extends Animal + |} + | + | val animal: Id[Animal] = ??? + | + |animal match + |\tcase Cat => $$0 + |\tcase Dog => + | + |} + |""".stripMargin, + filter = _.contains("exhaustive"), + ) From add9027e8e28f0cc91d3f100df027f28912d8c10 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 3 Oct 2023 17:53:49 +0200 Subject: [PATCH 080/134] Fix wunused false positive on CanEqual [Cherry-picked fa72e70cba3763119af151dc7a565b5606268689] --- .../tools/dotc/transform/CheckUnused.scala | 22 ++++++++++++++----- tests/pos/i17762.scala | 21 ++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i17762.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 3b397d8be746..736b9fbc43bb 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -1,6 +1,7 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser} import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.ast.untpd.ImportSelector @@ -423,12 +424,12 @@ object CheckUnused: if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then impInScope.top += imp unusedImport ++= imp.selectors.filter { s => - !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) + !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) && !isImportIgnored(imp, s) } /** Register (or not) some `val` or `def` according to the context, scope and flags */ def registerDef(memDef: tpd.MemberDef)(using Context): Unit = - if memDef.isValidMemberDef then + if memDef.isValidMemberDef && !isDefIgnored(memDef) then if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then if !paramsToSkip.contains(memDef.symbol) then @@ -507,7 +508,6 @@ object CheckUnused: def getUnused(using Context): UnusedResult = popScope() - val sortedImp = if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then unusedImport.map(d => UnusedSymbol(d.srcPos, d.name, WarnTypes.Imports)).toList @@ -643,9 +643,21 @@ object CheckUnused: sel.isWildcard || imp.expr.tpe.member(sel.name.toTermName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) || imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) - ) - + ) + + /** + * Ignore CanEqual imports + */ + private def isImportIgnored(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = + (sel.isWildcard && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) || + (imp.expr.tpe.member(sel.name.toTermName).alternatives + .exists(p => p.symbol.isOneOf(GivenOrImplicit) && p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) + /** + * Ignore definitions of CanEqual given + */ + private def isDefIgnored(memDef: tpd.MemberDef)(using Context): Boolean = + memDef.symbol.isOneOf(GivenOrImplicit) && memDef.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)) extension (tree: ImportSelector) def boundTpe: Type = tree.bound match { diff --git a/tests/pos/i17762.scala b/tests/pos/i17762.scala new file mode 100644 index 000000000000..65275c4619db --- /dev/null +++ b/tests/pos/i17762.scala @@ -0,0 +1,21 @@ +//> using options -Xfatal-warnings -Wunused:all + +class SomeType + +def testIt(st1: SomeType, st2: SomeType): Boolean = + given CanEqual[SomeType, SomeType] = CanEqual.derived + st1 == st2 + +object HasCanEqual: + given f: CanEqual[SomeType, SomeType] = CanEqual.derived + +object UsesCanEqual: + import HasCanEqual.given + def testIt(st1: SomeType, st2: SomeType): Boolean = + st1 == st2 + +object UsesCanEqual2: + import HasCanEqual.f + + def testIt(st1: SomeType, st2: SomeType): Boolean = + st1 == st2 \ No newline at end of file From fb563aa0f601e760fc8f64c89e911c465e28a50f Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 9 Oct 2023 14:25:22 +0200 Subject: [PATCH 081/134] Apply Jamie's suggestion [Cherry-picked 88eed71b4d3fd70e4bbbf7b8f4234e59069d30e0] --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 736b9fbc43bb..073626b4b5c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -649,7 +649,7 @@ object CheckUnused: * Ignore CanEqual imports */ private def isImportIgnored(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = - (sel.isWildcard && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) || + (sel.isWildcard && sel.isGiven && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)) && p.symbol.isOneOf(GivenOrImplicit))) || (imp.expr.tpe.member(sel.name.toTermName).alternatives .exists(p => p.symbol.isOneOf(GivenOrImplicit) && p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) From 4c52e1798f4e29e3da43d8e4db55221c419a7493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 5 Oct 2023 11:37:19 +0200 Subject: [PATCH 082/134] Fix #18649: Use loBound of param types when materializing a context function. Since the param types come from type arguments to `ContextFunctionN[...]`, nothing prevents them a priori from being wildcard type params, i.e., `TypeBounds`. However, that is not a valid type to give to a concrete term param. We can arbitrarily choose any type that conforms to the bounds, which realistically means one of the two bounds. Since type inference already chooses the lower bound when explicitly writing the context function, we align and choose the lower bound when materializing it. [Cherry-picked 242e68f68623b153718ecdc47021f989dfc80f95] --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 8 ++++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/pos/i18649.scala | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18649.scala diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 20290a2ee1f7..b72fcf646aa5 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -571,6 +571,14 @@ object TreeChecker { super.typedClassDef(cdef, cls) } + override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = + val tpdTree = super.typedValDef(vdef, sym) + vdef.tpt.tpe match + case _: ValueType => () // ok + case _: ExprType if sym.isOneOf(TermParamOrAccessor) => () // ok + case _ => assert(false, i"wrong type, expected a value type for ${sym.fullName}, but found: ${sym.info}") + tpdTree + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree = def defParamss = ddef.paramss.filter(!_.isEmpty).nestedMap(_.symbol) def layout(symss: List[List[Symbol]]): String = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c18a369207a5..13544c32cac2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3160,7 +3160,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val paramTypes = { val hasWildcard = formals.exists(_.existsPart(_.isInstanceOf[WildcardType], StopAt.Static)) if hasWildcard then formals.map(_ => untpd.TypeTree()) - else formals.map(untpd.TypeTree) + else formals.map(formal => untpd.TypeTree(formal.loBound)) // about loBound, see tests/pos/i18649.scala } val erasedParams = pt match { diff --git a/tests/pos/i18649.scala b/tests/pos/i18649.scala new file mode 100644 index 000000000000..d013d5219a1e --- /dev/null +++ b/tests/pos/i18649.scala @@ -0,0 +1,7 @@ +object Test: + // always inferred Nothing for `x` + def contextFunctionWildcardExplicit: ? ?=> String = x ?=> "foo" + + // used to infer TYPEBOUNDS for the type of the argument + def contextFunctionWildcardInserted: ? ?=> String = "foo" +end Test From e056b2eb465db71a8f2ff14b49dc528415fed49b Mon Sep 17 00:00:00 2001 From: John Duffell Date: Thu, 12 Oct 2023 14:00:48 +0100 Subject: [PATCH 083/134] don't try to complete scala 2 macro before the constructor (and its type params) have been completed [Cherry-picked 974e4d3c4e965c71601d6e1de87cd641c9fc1387] --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- tests/pos-macros/i16630.scala | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i16630.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 2ee750acc84f..6507695b95df 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1030,7 +1030,7 @@ object SymDenotations { /** Is this a Scala 2 macro defined */ final def isScala2MacroInScala3(using Context): Boolean = - is(Macro, butNot = Inline) && is(Erased) + is(Macro, butNot = Inline) && flagsUNSAFE.is(Erased) // flag is set initially for macros - we check if it's a scala 2 macro before completing the type constructor so do not force the info to check the flag // Consider the macros of StringContext as plain Scala 2 macros when // compiling the standard library with Dotty. // This should be removed on Scala 3.x diff --git a/tests/pos-macros/i16630.scala b/tests/pos-macros/i16630.scala new file mode 100644 index 000000000000..50e80ed996c1 --- /dev/null +++ b/tests/pos-macros/i16630.scala @@ -0,0 +1,14 @@ +import scala.language.experimental.macros +import scala.quoted.{Quotes, Expr, Type} + +trait TraitWithTypeParam[A]: + inline def foo: Option[A] = ${ MacrosImpl.fooImpl[A] } + def foo: Option[A] = macro MacrosImpl.compatFooImpl[A] + +object MacrosImpl: + def fooImpl[A: Type](using quotes: Quotes): Expr[Option[A]] = ??? + def compatFooImpl[A: c.WeakTypeTag](c: Context): c.Tree = ??? + +trait Context: + type WeakTypeTag[A] + type Tree \ No newline at end of file From 82ff24f8fdaa6b0bd743dae202bd8272ee041aad Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Jun 2024 15:17:31 +0200 Subject: [PATCH 084/134] Check user defined PolyFunction refinements `PolyFunction` must be refined with an `apply` method that has a single parameter list with no by-name nor varargs parameters. It may optionally have type parameters. Some of these restrictions could be lifted later, but for now these features are not properly handled by the compiler. Fixes #8299 Fixes #18302 [Cherry-picked e5ca0c42f85bff00f9fdd6e79f8db293d84966dd][modified] --- .../dotty/tools/dotc/core/Definitions.scala | 11 ++++++++ .../tools/dotc/transform/PostTyper.scala | 5 ++++ .../src/dotty/tools/dotc/typer/Checking.scala | 25 +++++++++++++++++++ tests/neg/i18302b.check | 4 +++ tests/neg/i18302b.scala | 5 ++++ tests/neg/i18302c.check | 4 +++ tests/neg/i18302c.scala | 5 ++++ tests/neg/i18302d.check | 4 +++ tests/neg/i18302d.scala | 2 ++ tests/neg/i18302e.check | 8 ++++++ tests/neg/i18302e.scala | 4 +++ tests/neg/i18302f.check | 12 +++++++++ tests/neg/i18302f.scala | 7 ++++++ tests/neg/i18302i.scala | 6 +++++ tests/neg/i18302j.scala | 5 ++++ tests/neg/i8299.scala | 8 ++++++ tests/pos/i18302a.scala | 4 +++ 17 files changed, 119 insertions(+) create mode 100644 tests/neg/i18302b.check create mode 100644 tests/neg/i18302b.scala create mode 100644 tests/neg/i18302c.check create mode 100644 tests/neg/i18302c.scala create mode 100644 tests/neg/i18302d.check create mode 100644 tests/neg/i18302d.scala create mode 100644 tests/neg/i18302e.check create mode 100644 tests/neg/i18302e.scala create mode 100644 tests/neg/i18302f.check create mode 100644 tests/neg/i18302f.scala create mode 100644 tests/neg/i18302i.scala create mode 100644 tests/neg/i18302j.scala create mode 100644 tests/neg/i8299.scala create mode 100644 tests/pos/i18302a.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index c5a798e2dcd7..6ee40c9f9706 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1157,6 +1157,17 @@ class Definitions { if tpe.refinedName == nme.apply && tpe.parent.derivesFrom(defn.PolyFunctionClass) => Some(mt) case _ => None + + def isValidPolyFunctionInfo(info: Type)(using Context): Boolean = + def isValidMethodType(info: Type) = info match + case info: MethodType => + !info.resType.isInstanceOf[MethodOrPoly] && // Has only one parameter list + !info.isVarArgsMethod && + !info.paramInfos.exists(_.isInstanceOf[ExprType]) // No by-name parameters + case _ => false + info match + case info: PolyType => isValidMethodType(info.resType) + case _ => isValidMethodType(info) } object ErasedFunctionOf { diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 822d679b4954..39f8ae6e757b 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -375,6 +375,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: ValDef => registerIfHasMacroAnnotations(tree) checkErasedDef(tree) + Checking.checkPolyFunctionType(tree.tpt) val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) if tree1.removeAttachment(desugar.UntupledParam).isDefined then checkStableSelection(tree.rhs) @@ -382,6 +383,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: DefDef => registerIfHasMacroAnnotations(tree) checkErasedDef(tree) + Checking.checkPolyFunctionType(tree.tpt) annotateContextResults(tree) val tree1 = cpy.DefDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef])) @@ -483,6 +485,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) + case tree: RefinedTypeTree => + Checking.checkPolyFunctionType(tree) + super.transform(tree) case _: Quote => ctx.compilationUnit.needsStaging = true super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 93acc01d28ad..3948fecb2a0e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -804,6 +804,31 @@ object Checking { else Feature.checkExperimentalFeature("features", imp.srcPos) case _ => end checkExperimentalImports + + /** Checks that PolyFunction only have valid refinements. + * + * It only supports `apply` methods with one parameter list and optional type arguments. + */ + def checkPolyFunctionType(tree: Tree)(using Context): Unit = new TreeTraverser { + def traverse(tree: Tree)(using Context): Unit = tree match + case tree: RefinedTypeTree if tree.tpe.derivesFrom(defn.PolyFunctionClass) => + if tree.refinements.isEmpty then + reportNoRefinements(tree.srcPos) + tree.refinements.foreach { + case refinement: DefDef if refinement.name != nme.apply => + report.error("PolyFunction only supports apply method refinements", refinement.srcPos) + case refinement: DefDef if !defn.PolyFunctionOf.isValidPolyFunctionInfo(refinement.tpe.widen) => + report.error("Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.", refinement.srcPos) + case _ => + } + case _: RefTree if tree.symbol == defn.PolyFunctionClass => + reportNoRefinements(tree.srcPos) + case _ => + traverseChildren(tree) + + def reportNoRefinements(pos: SrcPos) = + report.error("PolyFunction subtypes must refine the apply method", pos) + }.traverse(tree) } trait Checking { diff --git a/tests/neg/i18302b.check b/tests/neg/i18302b.check new file mode 100644 index 000000000000..0dc3ba6c054a --- /dev/null +++ b/tests/neg/i18302b.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i18302b.scala:3:32 --------------------------------------------------------------------------------- +3 |def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed. diff --git a/tests/neg/i18302b.scala b/tests/neg/i18302b.scala new file mode 100644 index 000000000000..71c7992c178b --- /dev/null +++ b/tests/neg/i18302b.scala @@ -0,0 +1,5 @@ +def test = polyFun(1)(2) + +def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error + new PolyFunction: + def apply(x: Int)(y: Int): Int = x + y diff --git a/tests/neg/i18302c.check b/tests/neg/i18302c.check new file mode 100644 index 000000000000..4610145a30b2 --- /dev/null +++ b/tests/neg/i18302c.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i18302c.scala:4:32 --------------------------------------------------------------------------------- +4 |def polyFun: PolyFunction { def foo(x: Int): Int } = // error + | ^^^^^^^^^^^^^^^^^^^^ + | PolyFunction only supports apply method refinements diff --git a/tests/neg/i18302c.scala b/tests/neg/i18302c.scala new file mode 100644 index 000000000000..a5d182d7ad0c --- /dev/null +++ b/tests/neg/i18302c.scala @@ -0,0 +1,5 @@ +import scala.reflect.Selectable.reflectiveSelectable + +def test = polyFun.foo(1) +def polyFun: PolyFunction { def foo(x: Int): Int } = // error + new PolyFunction { def foo(x: Int): Int = x + 1 } diff --git a/tests/neg/i18302d.check b/tests/neg/i18302d.check new file mode 100644 index 000000000000..976db59763c1 --- /dev/null +++ b/tests/neg/i18302d.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i18302d.scala:1:32 --------------------------------------------------------------------------------- +1 |def polyFun: PolyFunction { def apply: Int } = // error + | ^^^^^^^^^^^^^^ + |Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed. diff --git a/tests/neg/i18302d.scala b/tests/neg/i18302d.scala new file mode 100644 index 000000000000..a7f9a5bec286 --- /dev/null +++ b/tests/neg/i18302d.scala @@ -0,0 +1,2 @@ +def polyFun: PolyFunction { def apply: Int } = // error + new PolyFunction { def apply: Int = 1 } diff --git a/tests/neg/i18302e.check b/tests/neg/i18302e.check new file mode 100644 index 000000000000..aae101875845 --- /dev/null +++ b/tests/neg/i18302e.check @@ -0,0 +1,8 @@ +-- Error: tests/neg/i18302e.scala:1:13 --------------------------------------------------------------------------------- +1 |def polyFun: PolyFunction { } = // error + | ^^^^^^^^^^^^^^^^^ + | PolyFunction subtypes must refine the apply method +-- Error: tests/neg/i18302e.scala:4:15 --------------------------------------------------------------------------------- +4 |def polyFun(f: PolyFunction { }) = () // error + | ^^^^^^^^^^^^^^^^^ + | PolyFunction subtypes must refine the apply method diff --git a/tests/neg/i18302e.scala b/tests/neg/i18302e.scala new file mode 100644 index 000000000000..1ffab2586048 --- /dev/null +++ b/tests/neg/i18302e.scala @@ -0,0 +1,4 @@ +def polyFun: PolyFunction { } = // error + new PolyFunction { } + +def polyFun(f: PolyFunction { }) = () // error diff --git a/tests/neg/i18302f.check b/tests/neg/i18302f.check new file mode 100644 index 000000000000..df0d76c2f157 --- /dev/null +++ b/tests/neg/i18302f.check @@ -0,0 +1,12 @@ +-- Error: tests/neg/i18302f.scala:1:13 --------------------------------------------------------------------------------- +1 |def polyFun: PolyFunction = // error + | ^^^^^^^^^^^^ + | PolyFunction subtypes must refine the apply method +-- Error: tests/neg/i18302f.scala:4:16 --------------------------------------------------------------------------------- +4 |def polyFun2(a: PolyFunction) = () // error + | ^^^^^^^^^^^^ + | PolyFunction subtypes must refine the apply method +-- Error: tests/neg/i18302f.scala:6:14 --------------------------------------------------------------------------------- +6 |val polyFun3: PolyFunction = // error + | ^^^^^^^^^^^^ + | PolyFunction subtypes must refine the apply method diff --git a/tests/neg/i18302f.scala b/tests/neg/i18302f.scala new file mode 100644 index 000000000000..2f86f0e1eb62 --- /dev/null +++ b/tests/neg/i18302f.scala @@ -0,0 +1,7 @@ +def polyFun: PolyFunction = // error + new PolyFunction { } + +def polyFun2(a: PolyFunction) = () // error + +val polyFun3: PolyFunction = // error + new PolyFunction { } diff --git a/tests/neg/i18302i.scala b/tests/neg/i18302i.scala new file mode 100644 index 000000000000..e64330879e55 --- /dev/null +++ b/tests/neg/i18302i.scala @@ -0,0 +1,6 @@ +def polyFun1: Option[PolyFunction] = ??? // error +def polyFun2: PolyFunction & Any = ??? // error +def polyFun3: Any & PolyFunction = ??? // error +def polyFun4: PolyFunction | Any = ??? // error +def polyFun5: Any | PolyFunction = ??? // error +def polyFun6(a: Any | PolyFunction) = ??? // error diff --git a/tests/neg/i18302j.scala b/tests/neg/i18302j.scala new file mode 100644 index 000000000000..8c63aa573c9b --- /dev/null +++ b/tests/neg/i18302j.scala @@ -0,0 +1,5 @@ +def polyFunByName: PolyFunction { def apply(thunk: => Int): Int } = // error + new PolyFunction { def apply(thunk: => Int): Int = 1 } + +def polyFunVarArgs: PolyFunction { def apply(args: Int*): Int } = // error + new PolyFunction { def apply(thunk: Int*): Int = 1 } diff --git a/tests/neg/i8299.scala b/tests/neg/i8299.scala new file mode 100644 index 000000000000..e3e41515ff29 --- /dev/null +++ b/tests/neg/i8299.scala @@ -0,0 +1,8 @@ +package example + +object Main { + def main(a: Array[String]): Unit = { + val p: PolyFunction = // error: PolyFunction subtypes must refine the apply method + [A] => (xs: List[A]) => xs.headOption + } +} diff --git a/tests/pos/i18302a.scala b/tests/pos/i18302a.scala new file mode 100644 index 000000000000..c087b63543f4 --- /dev/null +++ b/tests/pos/i18302a.scala @@ -0,0 +1,4 @@ +def test = polyFun(1) + +def polyFun: PolyFunction { def apply(x: Int): Int } = + new PolyFunction { def apply(x: Int): Int = x + 1 } From 54f5421accffbfcf4f8ff23ca247da5833bd4512 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 25 Aug 2023 16:50:02 +0200 Subject: [PATCH 085/134] Add `isMethodWithByNameArgs` [Cherry-picked 9966ced284fec8a7e601c3de1edd328a10a451fc] --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 6 ++++++ compiler/src/dotty/tools/dotc/typer/Nullables.scala | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6ee40c9f9706..e9b8d2e7affa 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1163,7 +1163,7 @@ class Definitions { case info: MethodType => !info.resType.isInstanceOf[MethodOrPoly] && // Has only one parameter list !info.isVarArgsMethod && - !info.paramInfos.exists(_.isInstanceOf[ExprType]) // No by-name parameters + !info.isMethodWithByNameArgs // No by-name parameters case _ => false info match case info: PolyType => isValidMethodType(info.resType) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8b2749fc1254..545bb138969a 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -419,6 +419,12 @@ object Types { case _ => false } + /** Is this the type of a method that has a by-name parameters? */ + def isMethodWithByNameArgs(using Context): Boolean = stripPoly match { + case mt: MethodType => mt.paramInfos.exists(_.isInstanceOf[ExprType]) + case _ => false + } + /** Is this the type of a method with a leading empty parameter list? */ def isNullaryMethod(using Context): Boolean = stripPoly match { diff --git a/compiler/src/dotty/tools/dotc/typer/Nullables.scala b/compiler/src/dotty/tools/dotc/typer/Nullables.scala index 9104418d406f..722dc2186693 100644 --- a/compiler/src/dotty/tools/dotc/typer/Nullables.scala +++ b/compiler/src/dotty/tools/dotc/typer/Nullables.scala @@ -507,7 +507,7 @@ object Nullables: def postProcessByNameArgs(fn: TermRef, app: Tree)(using Context): Tree = fn.widen match case mt: MethodType - if mt.paramInfos.exists(_.isInstanceOf[ExprType]) && !fn.symbol.is(Inline) => + if mt.isMethodWithByNameArgs && !fn.symbol.is(Inline) => app match case Apply(fn, args) => object dropNotNull extends TreeMap: From 8d05ace4cdb772650309470a81f34e059a555a69 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 28 Jun 2023 15:05:40 +0200 Subject: [PATCH 086/134] Check if a fatal warning issued in typer is silenced, before converting it into an error closes lampepfl#17741 closes lampepfl#17735 [Cherry-picked ad45c3e9b7843f2d6735816d955924e92702805c] --- .../dotty/tools/dotc/reporting/Reporter.scala | 24 +++++++------- .../refutable-pattern-binding-messages.check | 32 +++++++++---------- tests/neg/warn-value-discard.check | 16 +++++----- tests/pos-special/fatal-warnings/i17735.scala | 24 ++++++++++++++ .../pos-special/fatal-warnings/i17735a.scala | 24 ++++++++++++++ tests/pos-special/fatal-warnings/i17741.scala | 32 +++++++++++++++++++ .../fatal-warnings/nowarnannot.scala | 6 ++++ 7 files changed, 123 insertions(+), 35 deletions(-) create mode 100644 tests/pos-special/fatal-warnings/i17735.scala create mode 100644 tests/pos-special/fatal-warnings/i17735a.scala create mode 100644 tests/pos-special/fatal-warnings/i17741.scala create mode 100644 tests/pos-special/fatal-warnings/nowarnannot.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index f5aadac27296..f9e2cfd3ea3b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -149,14 +149,10 @@ abstract class Reporter extends interfaces.ReporterResult { val key = w.enablingOption.name addUnreported(key, 1) case _ => - // conditional warnings that are not enabled are not fatal - val d = dia match - case w: Warning if ctx.settings.XfatalWarnings.value => w.toError - case _ => dia - if !isHidden(d) then // avoid isHidden test for summarized warnings so that message is not forced - markReported(d) - withMode(Mode.Printing)(doReport(d)) - d match { + if !isHidden(dia) then // avoid isHidden test for summarized warnings so that message is not forced + markReported(dia) + withMode(Mode.Printing)(doReport(dia)) + dia match { case _: Warning => _warningCount += 1 case e: Error => errors = e :: errors @@ -169,13 +165,19 @@ abstract class Reporter extends interfaces.ReporterResult { end issueUnconfigured def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit = + def toErrorIfFatal(dia: Diagnostic) = dia match + case w: Warning if ctx.settings.silentWarnings.value => dia + case w: ConditionalWarning if w.isSummarizedConditional => dia + case w: Warning if ctx.settings.XfatalWarnings.value => w.toError + case _ => dia + def go() = import Action._ dia match - case w: Warning => WConf.parsed.action(w) match + case w: Warning => WConf.parsed.action(dia) match case Error => issueUnconfigured(w.toError) - case Warning => issueUnconfigured(w) - case Verbose => issueUnconfigured(w.setVerbose()) + case Warning => issueUnconfigured(toErrorIfFatal(w)) + case Verbose => issueUnconfigured(toErrorIfFatal(w.setVerbose())) case Info => issueUnconfigured(w.toInfo) case Silent => case _ => issueUnconfigured(dia) diff --git a/tests/neg/refutable-pattern-binding-messages.check b/tests/neg/refutable-pattern-binding-messages.check index 5a9d85fd4447..b1b8866e174f 100644 --- a/tests/neg/refutable-pattern-binding-messages.check +++ b/tests/neg/refutable-pattern-binding-messages.check @@ -1,3 +1,11 @@ +-- Error: tests/neg/refutable-pattern-binding-messages.scala:5:14 ------------------------------------------------------ +5 | val Positive(p) = 5 // error: refutable extractor + | ^^^^^^^^^^^^^^^ + | pattern binding uses refutable extractor `Test.Positive` + | + | If this usage is intentional, this can be communicated by adding `: @unchecked` after the expression, + | which may result in a MatchError at runtime. + | This patch can be rewritten automatically under -rewrite -source 3.2-migration. -- Error: tests/neg/refutable-pattern-binding-messages.scala:6:14 ------------------------------------------------------ 6 | for Positive(i) <- List(1, 2, 3) do () // error: refutable extractor | ^^^^^^^^^^^ @@ -6,6 +14,14 @@ | If this usage is intentional, this can be communicated by adding the `case` keyword before the full pattern, | which will result in a filtering for expression (using `withFilter`). | This patch can be rewritten automatically under -rewrite -source 3.2-migration. +-- Error: tests/neg/refutable-pattern-binding-messages.scala:10:20 ----------------------------------------------------- +10 | val i :: is = List(1, 2, 3) // error: pattern type more specialized + | ^^^^^^^^^^^^^ + | pattern's type ::[Int] is more specialized than the right hand side expression's type List[Int] + | + | If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, + | which may result in a MatchError at runtime. + | This patch can be rewritten automatically under -rewrite -source 3.2-migration. -- Error: tests/neg/refutable-pattern-binding-messages.scala:11:11 ----------------------------------------------------- 11 | for ((x: String) <- xs) do () // error: pattern type more specialized | ^^^^^^ @@ -22,22 +38,6 @@ | If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern, | which will result in a filtering for expression (using `withFilter`). | This patch can be rewritten automatically under -rewrite -source 3.2-migration. --- Error: tests/neg/refutable-pattern-binding-messages.scala:5:14 ------------------------------------------------------ -5 | val Positive(p) = 5 // error: refutable extractor - | ^^^^^^^^^^^^^^^ - | pattern binding uses refutable extractor `Test.Positive` - | - | If this usage is intentional, this can be communicated by adding `: @unchecked` after the expression, - | which may result in a MatchError at runtime. - | This patch can be rewritten automatically under -rewrite -source 3.2-migration. --- Error: tests/neg/refutable-pattern-binding-messages.scala:10:20 ----------------------------------------------------- -10 | val i :: is = List(1, 2, 3) // error: pattern type more specialized - | ^^^^^^^^^^^^^ - | pattern's type ::[Int] is more specialized than the right hand side expression's type List[Int] - | - | If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, - | which may result in a MatchError at runtime. - | This patch can be rewritten automatically under -rewrite -source 3.2-migration. -- Error: tests/neg/refutable-pattern-binding-messages.scala:16:10 ----------------------------------------------------- 16 | val 1 = 2 // error: pattern type does not match | ^ diff --git a/tests/neg/warn-value-discard.check b/tests/neg/warn-value-discard.check index ab6539dd5cd8..ba43c743709f 100644 --- a/tests/neg/warn-value-discard.check +++ b/tests/neg/warn-value-discard.check @@ -1,11 +1,3 @@ --- [E175] Potential Issue Error: tests/neg/warn-value-discard.scala:15:35 ---------------------------------------------- -15 | firstThing().map(_ => secondThing()) // error - | ^^^^^^^^^^^^^ - | discarded non-Unit value of type Either[Failed, Unit] --- [E175] Potential Issue Error: tests/neg/warn-value-discard.scala:18:35 ---------------------------------------------- -18 | firstThing().map(_ => secondThing()) // error - | ^^^^^^^^^^^^^ - | discarded non-Unit value of type Either[Failed, Unit] -- [E175] Potential Issue Error: tests/neg/warn-value-discard.scala:27:36 ---------------------------------------------- 27 | mutable.Set.empty[String].remove("") // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,3 +10,11 @@ 59 | mutable.Set.empty[String] += "" // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type scala.collection.mutable.Set[String] +-- [E175] Potential Issue Error: tests/neg/warn-value-discard.scala:15:35 ---------------------------------------------- +15 | firstThing().map(_ => secondThing()) // error + | ^^^^^^^^^^^^^ + | discarded non-Unit value of type Either[Failed, Unit] +-- [E175] Potential Issue Error: tests/neg/warn-value-discard.scala:18:35 ---------------------------------------------- +18 | firstThing().map(_ => secondThing()) // error + | ^^^^^^^^^^^^^ + | discarded non-Unit value of type Either[Failed, Unit] diff --git a/tests/pos-special/fatal-warnings/i17735.scala b/tests/pos-special/fatal-warnings/i17735.scala new file mode 100644 index 000000000000..56050d8fd5fd --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17735.scala @@ -0,0 +1,24 @@ +// scalac: -Wvalue-discard + +import scala.collection.mutable +import scala.annotation.nowarn + +object Foo: + + def f(b: Boolean): String = + val messageBuilder = mutable.StringBuilder() + if b then + // Here @nowarn is effective with or without -Wfatal-warnings + // i.e. no warning without -Wfatal-warnings and no error with -Wfatal-warnings + messageBuilder.append("helloworld").append("\n"): @nowarn("msg=discarded non-Unit value*") + + messageBuilder.result() + + def g(x: String => Unit) = ??? + def h: String = + val messageBuilder = mutable.StringBuilder() + g: s => + // here @nowarn is effective without -Wfatal-warnings (i.e. no warning) + // But with -Wfatal-warnings we get an error + messageBuilder.append("\n").append(s): @nowarn("msg=discarded non-Unit value*") + messageBuilder.result() \ No newline at end of file diff --git a/tests/pos-special/fatal-warnings/i17735a.scala b/tests/pos-special/fatal-warnings/i17735a.scala new file mode 100644 index 000000000000..d089763295e6 --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17735a.scala @@ -0,0 +1,24 @@ +// scalac: -Wvalue-discard -Wconf:msg=non-Unit:s + +import scala.collection.mutable +import scala.annotation.nowarn + +object Test: + + def f(b: Boolean): String = + val messageBuilder = mutable.StringBuilder() + if b then + // Here @nowarn is effective with or without -Wfatal-warnings + // i.e. no warning without -Wfatal-warnings and no error with -Wfatal-warnings + messageBuilder.append("helloworld").append("\n") + + messageBuilder.result() + + def g(x: String => Unit) = ??? + def h: String = + val messageBuilder = mutable.StringBuilder() + g: s => + // here @nowarn is effective without -Wfatal-warnings (i.e. no warning) + // But with -Wfatal-warnings we get an error + messageBuilder.append("\n").append(s) + messageBuilder.result() diff --git a/tests/pos-special/fatal-warnings/i17741.scala b/tests/pos-special/fatal-warnings/i17741.scala new file mode 100644 index 000000000000..07af8b1abd3d --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17741.scala @@ -0,0 +1,32 @@ +// scalac: -Wnonunit-statement + +class Node() +class Elem( + prefix: String, + label: String, + minimizeEmpty: Boolean, + child: Node* +) extends Node +class Text(text: String) extends Node +class NodeBuffer() { + def &+(node: Node): NodeBuffer = + this +} +class NodeSeq() +object NodeSeq { + def seqToNodeSeq(seq: NodeBuffer): Seq[Node] = ??? +} + +object Main { + def example() = { + { + new Elem(null, "foo", false, + { + val $buf: NodeBuffer = new NodeBuffer() + $buf.&+(new Text("bar")) + NodeSeq.seqToNodeSeq($buf) + }* + ) + } + }: @annotation.nowarn() +} \ No newline at end of file diff --git a/tests/pos-special/fatal-warnings/nowarnannot.scala b/tests/pos-special/fatal-warnings/nowarnannot.scala new file mode 100644 index 000000000000..26e9713d0543 --- /dev/null +++ b/tests/pos-special/fatal-warnings/nowarnannot.scala @@ -0,0 +1,6 @@ +case class F(i: Int) + +object Main { + def example() = + List(1, 2, 3).map(F): @annotation.nowarn +} From 3ff70455140eb33a571312c794583b98c4fa98f2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 12 Oct 2023 17:40:53 +0200 Subject: [PATCH 087/134] Remove double tests of `tests/pos` We should not tests all `tests/pos` files twice with another set of flags. Furthermore, in all our tests the result of adding the flags is not checked in any meaningful way. The only tests for this flag (tests/neg-custom-args/fatal-warnings/i17612b/i17612b.scala) does add the flag explicitly and checks the result already. See https://github.com/lampepfl/dotty/commit/46f2db6af69063d09538f0da1a3a9c5ba5f871d1 [Cherry-picked b8aae699f5a21f60f4d495c6c58137b1af69d318] --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c794b100bb42..c998dbec6721 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -33,7 +33,6 @@ class CompilationTests { compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init")), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), - compileFilesInDir("tests/pos", defaultOptions.and("-Xlint:private-shadow", "-Xlint:type-parameter-shadow")), compileFile("tests/pos-special/sourcepath/outer/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")), compileFilesInDir("tests/pos-scala2", defaultOptions.and("-source", "3.0-migration")), compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")), From 3aba8841c2c589d64048cdc0f881c1f6632cde28 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 19 Oct 2023 18:26:32 +0100 Subject: [PATCH 088/134] Fallback erasing term references [Cherry-picked f35261d250901e03e70090f3b1cc034bb4457114] --- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 3 +-- tests/pos/i18721.min/core.scala | 5 +++++ tests/pos/i18721.min/main.scala | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i18721.min/core.scala create mode 100644 tests/pos/i18721.min/main.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index baa6bf21e64e..fe76ab25ceb7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -239,8 +239,7 @@ object TypeErasure { * TermRefs are kept instead of being widened away. */ def erasedRef(tp: Type)(using Context): Type = tp match { - case tp: TermRef => - assert(tp.symbol.exists, tp) + case tp: TermRef if tp.symbol.exists => val tp1 = makePackageObjPrefixExplicit(tp) if (tp1 ne tp) erasedRef(tp1) else TermRef(erasedRef(tp.prefix), tp.symbol.asTerm) diff --git a/tests/pos/i18721.min/core.scala b/tests/pos/i18721.min/core.scala new file mode 100644 index 000000000000..02b172fdd73b --- /dev/null +++ b/tests/pos/i18721.min/core.scala @@ -0,0 +1,5 @@ +class Foo +class Bar extends Selectable: + def selectDynamic(name: String): Foo = null + +inline def meth(inline foo: Foo): Foo = foo diff --git a/tests/pos/i18721.min/main.scala b/tests/pos/i18721.min/main.scala new file mode 100644 index 000000000000..a62ee8b0562b --- /dev/null +++ b/tests/pos/i18721.min/main.scala @@ -0,0 +1,3 @@ +class Test: + def t1(bar: Bar { val foo: Foo }): Any = + meth(bar.foo) From 5c0ec3696ead380b34a1063583f902950fadd9ab Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 16 Aug 2023 13:41:12 +0200 Subject: [PATCH 089/134] Improve error message for inaccessible types [Cherry-picked f4137f2bb664de32c97e07912eda31501ffd8f8c] --- .../dotty/tools/dotc/reporting/messages.scala | 2 +- tests/neg/i12573.check | 4 ++-- tests/neg/not-accessible.check | 20 +++++++++++++++++++ tests/neg/not-accessible.scala | 15 ++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 tests/neg/not-accessible.check create mode 100644 tests/neg/not-accessible.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 007f0a56f8d8..c6ad4648b69a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2961,7 +2961,7 @@ extends ReferenceMsg(CannotBeAccessedID): i"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" case _ => i"none of the overloaded alternatives named $name can" - val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" + val where = if (ctx.owner.exists) i" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) i"$whatCanNot be accessed as a member of $pre$where.$whyNot" diff --git a/tests/neg/i12573.check b/tests/neg/i12573.check index d250f4beabbe..a1774ed00bd7 100644 --- a/tests/neg/i12573.check +++ b/tests/neg/i12573.check @@ -4,6 +4,6 @@ | value getDFType is not a member of DFBits[(8 : Int)]. | Extension methods were tried, but the search failed with: | - | method getDFType cannot be accessed as a member of DFType.type from module class i12573$package$. + | method getDFType cannot be accessed as a member of DFType.type from package object i12573$package. | Access to protected method getDFType not permitted because enclosing package object i12573$package - | is not a subclass of object DFType where target is defined \ No newline at end of file + | is not a subclass of object DFType where target is defined diff --git a/tests/neg/not-accessible.check b/tests/neg/not-accessible.check new file mode 100644 index 000000000000..e97468aade3b --- /dev/null +++ b/tests/neg/not-accessible.check @@ -0,0 +1,20 @@ +-- [E173] Reference Error: tests/neg/not-accessible.scala:8:23 --------------------------------------------------------- +8 | def test(a: A) = a.x // error + | ^^^ + | value x cannot be accessed as a member of (a : foo.A) from class B. +-- [E173] Reference Error: tests/neg/not-accessible.scala:10:23 -------------------------------------------------------- +10 | def test(a: A) = a.x // error + | ^^^ + | value x cannot be accessed as a member of (a : foo.A) from object B. +-- [E173] Reference Error: tests/neg/not-accessible.scala:13:23 -------------------------------------------------------- +13 | def test(a: A) = a.x // error + | ^^^ + | value x cannot be accessed as a member of (a : foo.A) from package object not-accessible$package. +-- [E173] Reference Error: tests/neg/not-accessible.scala:5:21 --------------------------------------------------------- +5 | def test(a: A) = a.x // error + | ^^^ + | value x cannot be accessed as a member of (a : foo.A) from package object not-accessible$package. +-- [E173] Reference Error: tests/neg/not-accessible.scala:15:23 -------------------------------------------------------- +15 |def test(a: foo.A) = a.x // error + | ^^^ + | value x cannot be accessed as a member of (a : foo.A) from package object not-accessible$package. diff --git a/tests/neg/not-accessible.scala b/tests/neg/not-accessible.scala new file mode 100644 index 000000000000..a0ff791e966f --- /dev/null +++ b/tests/neg/not-accessible.scala @@ -0,0 +1,15 @@ +package foo: + + class A(private[A] val x: Int) + + def test(a: A) = a.x // error + + class B: + def test(a: A) = a.x // error + object B: + def test(a: A) = a.x // error + + package bar: + def test(a: A) = a.x // error + +def test(a: foo.A) = a.x // error From 82eed5fdcc7ca060ec3abedac38593f26ad9af3a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 17 Aug 2023 10:09:14 +0200 Subject: [PATCH 090/134] Pretty print top-level definitions object symbol [Cherry-picked 1920336f78152958d13cbdb476cc73aff63cdd59] --- compiler/src/dotty/tools/dotc/core/NameOps.scala | 12 +++++++++++- .../src/dotty/tools/dotc/core/SymDenotations.scala | 4 ++++ .../dotty/tools/dotc/printing/RefinedPrinter.scala | 7 ++++++- tests/neg-macros/annot-result-owner.check | 2 +- tests/neg/i12573.check | 10 +++++----- tests/neg/not-accessible.check | 6 +++--- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 04440c9e9b39..644cbfbdb5a8 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -109,7 +109,7 @@ object NameOps { false } - /** is this the name of an object enclosing packagel-level definitions? */ + /** is this the name of an object enclosing package-level definitions? */ def isPackageObjectName: Boolean = name match { case name: TermName => name == nme.PACKAGE || name.endsWith(str.TOPLEVEL_SUFFIX) case name: TypeName => @@ -119,6 +119,16 @@ object NameOps { } } + /** is this the name of an object enclosing top-level definitions? */ + def isTopLevelPackageObjectName: Boolean = name match { + case name: TermName => name.endsWith(str.TOPLEVEL_SUFFIX) + case name: TypeName => + name.toTermName match { + case ModuleClassName(original) => original.isTopLevelPackageObjectName + case _ => false + } + } + /** Convert this module name to corresponding module class name */ def moduleClassName: TypeName = name.derived(ModuleClassName).toTypeName diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6507695b95df..75bdf6132c30 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -672,6 +672,10 @@ object SymDenotations { def isPackageObject(using Context): Boolean = name.isPackageObjectName && owner.is(Package) && this.is(Module) + /** Is this symbol a package object containing top-level definitions? */ + def isTopLevelDefinitionsObject(using Context): Boolean = + name.isTopLevelPackageObjectName && owner.is(Package) && this.is(Module) + /** Is this symbol a toplevel definition in a package object? */ def isWrappedToplevelDef(using Context): Boolean = !isConstructor && owner.isPackageObject diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5685ca1ca671..472ab4a9aac6 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -1099,13 +1099,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { fullNameString(sym) else if (sym.is(ModuleClass) && sym.isPackageObject && sym.name.stripModuleClassSuffix == tpnme.PACKAGE) nameString(sym.owner.name) + else if (sym.is(ModuleClass) && sym.isTopLevelDefinitionsObject) + nameString(sym.owner.name) else if (sym.is(ModuleClass)) nameString(sym.name.stripModuleClassSuffix) + idString(sym) else if (hasMeaninglessName(sym)) simpleNameString(sym.owner) + idString(sym) else nameString(sym) - (keywordText(kindString(sym)) ~~ { + + if sym.is(ModuleClass) && sym.isTopLevelDefinitionsObject then + "top-level definition in package " + nameString(sym.owner.name) + else (keywordText(kindString(sym)) ~~ { if (sym.isAnonymousClass) toTextParents(sym.info.parents) ~~ "{...}" else diff --git a/tests/neg-macros/annot-result-owner.check b/tests/neg-macros/annot-result-owner.check index 5d67be058fdf..7064f62d8970 100644 --- a/tests/neg-macros/annot-result-owner.check +++ b/tests/neg-macros/annot-result-owner.check @@ -2,7 +2,7 @@ -- Error: tests/neg-macros/annot-result-owner/Test_2.scala:1:0 --------------------------------------------------------- 1 |@insertVal // error |^^^^^^^^^^ - |macro annotation @insertVal added value definitionWithWrongOwner$macro$1 with an inconsistent owner. Expected it to be owned by package object Test_2$package but was owned by method foo. + |macro annotation @insertVal added value definitionWithWrongOwner$macro$1 with an inconsistent owner. Expected it to be owned by top-level definition in package but was owned by method foo. -- Error: tests/neg-macros/annot-result-owner/Test_2.scala:5:2 --------------------------------------------------------- 5 | @insertVal // error | ^^^^^^^^^^ diff --git a/tests/neg/i12573.check b/tests/neg/i12573.check index a1774ed00bd7..fa402273cada 100644 --- a/tests/neg/i12573.check +++ b/tests/neg/i12573.check @@ -1,9 +1,9 @@ -- [E008] Not Found Error: tests/neg/i12573.scala:23:38 ---------------------------------------------------------------- 23 |val w: Value[8] = DFBits(Value[8](8)).getDFType.width // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | value getDFType is not a member of DFBits[(8 : Int)]. - | Extension methods were tried, but the search failed with: + |value getDFType is not a member of DFBits[(8 : Int)]. + |Extension methods were tried, but the search failed with: | - | method getDFType cannot be accessed as a member of DFType.type from package object i12573$package. - | Access to protected method getDFType not permitted because enclosing package object i12573$package - | is not a subclass of object DFType where target is defined + | method getDFType cannot be accessed as a member of DFType.type from top-level definition in package . + | Access to protected method getDFType not permitted because enclosing top-level definition in package + | is not a subclass of object DFType where target is defined diff --git a/tests/neg/not-accessible.check b/tests/neg/not-accessible.check index e97468aade3b..849c255aa68e 100644 --- a/tests/neg/not-accessible.check +++ b/tests/neg/not-accessible.check @@ -9,12 +9,12 @@ -- [E173] Reference Error: tests/neg/not-accessible.scala:13:23 -------------------------------------------------------- 13 | def test(a: A) = a.x // error | ^^^ - | value x cannot be accessed as a member of (a : foo.A) from package object not-accessible$package. + | value x cannot be accessed as a member of (a : foo.A) from top-level definition in package bar. -- [E173] Reference Error: tests/neg/not-accessible.scala:5:21 --------------------------------------------------------- 5 | def test(a: A) = a.x // error | ^^^ - | value x cannot be accessed as a member of (a : foo.A) from package object not-accessible$package. + | value x cannot be accessed as a member of (a : foo.A) from top-level definition in package foo. -- [E173] Reference Error: tests/neg/not-accessible.scala:15:23 -------------------------------------------------------- 15 |def test(a: foo.A) = a.x // error | ^^^ - | value x cannot be accessed as a member of (a : foo.A) from package object not-accessible$package. + | value x cannot be accessed as a member of (a : foo.A) from top-level definition in package . From b79bccf7c1aaa35645aeb7b0fd2d236599cd004a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 13 Oct 2023 10:18:13 +0200 Subject: [PATCH 091/134] Improve error message for inaccessible protected members [Cherry-picked 1472b7a4975106b224489e854cbedcb09db7ecb7] --- .../tools/dotc/core/SymDenotations.scala | 8 +++---- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- tests/neg-macros/annot-result-owner.check | 2 +- tests/neg/i12573.check | 5 ++-- tests/neg/i7709.check | 24 +++++++------------ tests/neg/not-accessible.check | 6 ++--- 6 files changed, 19 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 75bdf6132c30..cad15c96dcae 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -915,17 +915,17 @@ object SymDenotations { true else val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass + val location = if owner.is(Final) then owner.showLocated else owner.showLocated + " or one of its subclasses" fail(i""" - | Access to protected $this not permitted because enclosing ${encl.showLocated} - | is not a subclass of ${owner.showLocated} where target is defined""") + | Protected $this can only be accessed from $location.""") else if isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) then // allow accesses to types from arbitrary subclasses fixes #4737 // don't perform this check for static members true else + val location = if cls.is(Final) then cls.showLocated else cls.showLocated + " or one of its subclasses" fail(i""" - | Access to protected ${symbol.show} not permitted because prefix type ${pre.widen.show} - | does not conform to ${cls.showLocated} where the access takes place""") + | Protected $this can only be accessed from $location.""") end isProtectedAccessOK if pre eq NoPrefix then true diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 472ab4a9aac6..2bacbe7f9036 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -1109,7 +1109,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { nameString(sym) if sym.is(ModuleClass) && sym.isTopLevelDefinitionsObject then - "top-level definition in package " + nameString(sym.owner.name) + "the top-level definitions in package " + nameString(sym.owner.name) else (keywordText(kindString(sym)) ~~ { if (sym.isAnonymousClass) toTextParents(sym.info.parents) ~~ "{...}" diff --git a/tests/neg-macros/annot-result-owner.check b/tests/neg-macros/annot-result-owner.check index 7064f62d8970..e2209998579c 100644 --- a/tests/neg-macros/annot-result-owner.check +++ b/tests/neg-macros/annot-result-owner.check @@ -2,7 +2,7 @@ -- Error: tests/neg-macros/annot-result-owner/Test_2.scala:1:0 --------------------------------------------------------- 1 |@insertVal // error |^^^^^^^^^^ - |macro annotation @insertVal added value definitionWithWrongOwner$macro$1 with an inconsistent owner. Expected it to be owned by top-level definition in package but was owned by method foo. + |macro annotation @insertVal added value definitionWithWrongOwner$macro$1 with an inconsistent owner. Expected it to be owned by the top-level definitions in package but was owned by method foo. -- Error: tests/neg-macros/annot-result-owner/Test_2.scala:5:2 --------------------------------------------------------- 5 | @insertVal // error | ^^^^^^^^^^ diff --git a/tests/neg/i12573.check b/tests/neg/i12573.check index fa402273cada..8c744fda685b 100644 --- a/tests/neg/i12573.check +++ b/tests/neg/i12573.check @@ -4,6 +4,5 @@ |value getDFType is not a member of DFBits[(8 : Int)]. |Extension methods were tried, but the search failed with: | - | method getDFType cannot be accessed as a member of DFType.type from top-level definition in package . - | Access to protected method getDFType not permitted because enclosing top-level definition in package - | is not a subclass of object DFType where target is defined + | method getDFType cannot be accessed as a member of DFType.type from the top-level definitions in package . + | Protected method getDFType can only be accessed from object DFType. diff --git a/tests/neg/i7709.check b/tests/neg/i7709.check index 180cf1939d16..b3b4e21b9db9 100644 --- a/tests/neg/i7709.check +++ b/tests/neg/i7709.check @@ -2,47 +2,39 @@ 5 | class B extends X.Y // error | ^^^ | class Y cannot be accessed as a member of X.type from class B. - | Access to protected class Y not permitted because enclosing object A - | is not a subclass of object X where target is defined + | Protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:6:21 ------------------------------------------------------------------ 6 | class B2 extends X.Y: // error | ^^^ | class Y cannot be accessed as a member of X.type from class B2. - | Access to protected class Y not permitted because enclosing object A - | is not a subclass of object X where target is defined + | Protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:9:28 ------------------------------------------------------------------ 9 | class B4 extends B3(new X.Y) // error | ^^^ | class Y cannot be accessed as a member of X.type from class B4. - | Access to protected class Y not permitted because enclosing object A - | is not a subclass of object X where target is defined + | Protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:11:34 ----------------------------------------------------------------- 11 | def this(n: Int) = this(new X.Y().toString) // error | ^^^ | class Y cannot be accessed as a member of X.type from class B5. - | Access to protected class Y not permitted because enclosing object A - | is not a subclass of object X where target is defined + | Protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:13:20 ----------------------------------------------------------------- 13 | class B extends X.Y // error | ^^^ | class Y cannot be accessed as a member of X.type from class B. - | Access to protected class Y not permitted because enclosing trait T - | is not a subclass of object X where target is defined + | Protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:18:18 ----------------------------------------------------------------- 18 | def y = new xx.Y // error | ^^^^ | class Y cannot be accessed as a member of XX from class C. - | Access to protected class Y not permitted because enclosing class C - | is not a subclass of class XX where target is defined + | Protected class Y can only be accessed from class XX or one of its subclasses. -- [E173] Reference Error: tests/neg/i7709.scala:23:20 ----------------------------------------------------------------- 23 | def y = new xx.Y // error | ^^^^ | class Y cannot be accessed as a member of XX from class D. - | Access to protected class Y not permitted because enclosing class D - | is not a subclass of class XX where target is defined + | Protected class Y can only be accessed from class XX or one of its subclasses. -- [E173] Reference Error: tests/neg/i7709.scala:31:20 ----------------------------------------------------------------- 31 | class Q extends X.Y // error | ^^^ | class Y cannot be accessed as a member of p.X.type from class Q. - | Access to protected class Y not permitted because enclosing package p - | is not a subclass of object X in package p where target is defined + | Protected class Y can only be accessed from object X in package p. diff --git a/tests/neg/not-accessible.check b/tests/neg/not-accessible.check index 849c255aa68e..00206d281016 100644 --- a/tests/neg/not-accessible.check +++ b/tests/neg/not-accessible.check @@ -9,12 +9,12 @@ -- [E173] Reference Error: tests/neg/not-accessible.scala:13:23 -------------------------------------------------------- 13 | def test(a: A) = a.x // error | ^^^ - | value x cannot be accessed as a member of (a : foo.A) from top-level definition in package bar. + | value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package bar. -- [E173] Reference Error: tests/neg/not-accessible.scala:5:21 --------------------------------------------------------- 5 | def test(a: A) = a.x // error | ^^^ - | value x cannot be accessed as a member of (a : foo.A) from top-level definition in package foo. + | value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package foo. -- [E173] Reference Error: tests/neg/not-accessible.scala:15:23 -------------------------------------------------------- 15 |def test(a: foo.A) = a.x // error | ^^^ - | value x cannot be accessed as a member of (a : foo.A) from top-level definition in package . + | value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package . From 5d6211259db0c6a74733f0c1018d11c0526e801e Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 21 Jun 2024 16:13:34 +0200 Subject: [PATCH 092/134] Fix typos in Build.scala [Cherry-picked 9e6d1b250921828d57d6aebabf59666b42437e93][modified] --- project/Build.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index c6ef1b03e708..d12ab07d31d5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -790,7 +790,7 @@ object Build { "-classpath" :: classpath :: beforeCp ::: fromCp.drop(2) } - lazy val nonBootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( + lazy val nonBootstrappedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( // packageAll packages all and then returns a map with the abs location packageAll := Def.taskDyn { // Use a dynamic task to avoid loops when loading the settings Def.task { @@ -817,7 +817,7 @@ object Build { (Test / javaOptions) += "-Xss2m" ) - lazy val bootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( + lazy val bootstrappedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( javaOptions ++= { val jars = packageAll.value Seq( @@ -842,7 +842,7 @@ object Build { ) def dottyCompilerSettings(implicit mode: Mode): sbt.Def.SettingsDefinition = - if (mode == NonBootstrapped) nonBootstrapedDottyCompilerSettings else bootstrapedDottyCompilerSettings + if (mode == NonBootstrapped) nonBootstrappedDottyCompilerSettings else bootstrappedDottyCompilerSettings lazy val `scala3-compiler` = project.in(file("compiler")).asDottyCompiler(NonBootstrapped) @@ -1047,7 +1047,7 @@ object Build { /** Test the tasty generated by `stdlib-bootstrapped` * - * The tests are run with the bootstrapped compiler and the tasty inpector on the classpath. + * The tests are run with the bootstrapped compiler and the tasty inspector on the classpath. * The classpath has the default `scala-library` and not `stdlib-bootstrapped`. * * The jar of `stdlib-bootstrapped` is provided for to the tests. @@ -1448,14 +1448,14 @@ object Build { .asDottyBench(Bootstrapped) .settings(Jmh / run / mainClass := Some("org.openjdk.jmh.Main")) - val testcasesOutputDir = taskKey[Seq[String]]("Root directory where tests classses are generated") + val testcasesOutputDir = taskKey[Seq[String]]("Root directory where tests classes are generated") val testcasesSourceRoot = taskKey[String]("Root directory where tests sources are generated") val testDocumentationRoot = taskKey[String]("Root directory where tests documentation are stored") val generateSelfDocumentation = taskKey[Unit]("Generate example documentation") // Note: the two tasks below should be one, but a bug in Tasty prevents that val generateScalaDocumentation = inputKey[Unit]("Generate documentation for dotty lib") val generateStableScala3Documentation = inputKey[Unit]("Generate documentation for stable dotty lib") - val generateTestcasesDocumentation = taskKey[Unit]("Generate documentation for testcases, usefull for debugging tests") + val generateTestcasesDocumentation = taskKey[Unit]("Generate documentation for testcases, useful for debugging tests") val generateReferenceDocumentation = inputKey[Unit]("Generate language reference documentation for Scala 3") From ed1f2aff286094a09b7bcbe9e95e0439ccefe8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 6 Oct 2023 10:36:55 +0200 Subject: [PATCH 093/134] Fix #18658: Handle varargs of generic types in `JSExportsGen`. When extracting the type of a varargs parameter, we have to go back to before erasure. However, that gives us a non-erased type inside as well. We need to re-erase that type to get something sensible for the back-end. [Cherry-picked 45388755873246a8f5294b91dc3ad67ee2a8adfe] --- .../tools/dotc/transform/sjs/JSSymUtils.scala | 2 +- .../jsinterop/NonNativeJSTypeTestScala3.scala | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/sjs-junit/test/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTestScala3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala index 115d41dd3d46..ae6635bce622 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala @@ -185,7 +185,7 @@ object JSSymUtils { val list = for ((name, info) <- paramNamesAndTypes) yield { val v = - if (info.isRepeatedParam) Some(info.repeatedToSingle.widenDealias) + if (info.isRepeatedParam) Some(TypeErasure.erasure(info.repeatedToSingle)) else None name -> v } diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTestScala3.scala new file mode 100644 index 000000000000..ceb29d39b6a0 --- /dev/null +++ b/tests/sjs-junit/test/org/scalajs/testsuite/jsinterop/NonNativeJSTypeTestScala3.scala @@ -0,0 +1,78 @@ +package org.scalajs.testsuite.jsinterop + +import org.junit.Assert.* +import org.junit.Test + +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +class NonNativeJSTypeTestScala3 { + import NonNativeJSTypeTestScala3.* + + @Test + def overloadWithVarargOfGenericType(): Unit = { + class OverloadWithVarargOfGenericType extends js.Object { + def overloaded(x: Int): Int = x + def overloaded(xs: (Int, Int)*): Int = xs.size + } + + val obj = new OverloadWithVarargOfGenericType + assertEquals(5, obj.overloaded(5)) + assertEquals(1, obj.overloaded((5, 6))) + assertEquals(2, obj.overloaded((1, 2), (3, 4))) + } + + @Test + def overloadWithVarargOfValueClass(): Unit = { + class OverloadWithVarargOfValueClass extends js.Object { + def overloaded(x: Int): Int = x + def overloaded(xs: VC*): Int = xs.size + } + + val obj = new OverloadWithVarargOfValueClass + assertEquals(5, obj.overloaded(5)) + assertEquals(1, obj.overloaded(new VC(5))) + assertEquals(2, obj.overloaded(new VC(5), new VC(6))) + } + + @Test + def overloadWithVarargOfGenericValueClass(): Unit = { + class OverloadWithVarargOfGenericValueClass extends js.Object { + def overloaded(x: Int): Int = x + def overloaded(xs: GenVC[Int]*): Int = xs.size + } + + val obj = new OverloadWithVarargOfGenericValueClass + assertEquals(5, obj.overloaded(5)) + assertEquals(1, obj.overloaded(new GenVC(5))) + assertEquals(2, obj.overloaded(new GenVC(5), new GenVC(6))) + } + + @Test + def overloadWithVarargOfOpaqueTypeAlias(): Unit = { + import OpaqueContainer.* + + class OverloadWithVarargOfOpaqueTypeAlias extends js.Object { + def overloaded(x: String): Int = x.toInt + def overloaded(xs: OpaqueInt*): Int = xs.size + } + + val obj = new OverloadWithVarargOfOpaqueTypeAlias + assertEquals(5, obj.overloaded("5")) + assertEquals(1, obj.overloaded(fromInt(5))) + assertEquals(2, obj.overloaded(fromInt(5), fromInt(6))) + } +} + +object NonNativeJSTypeTestScala3 { + final class VC(val x: Int) extends AnyVal + + final class GenVC[T](val x: T) extends AnyVal + + object OpaqueContainer { + opaque type OpaqueInt = Int + + def fromInt(x: Int): OpaqueInt = x + def toInt(x: OpaqueInt): Int = x + } +} From 427c444457e56f4f8cf97526931a12716e25b4b4 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Mon, 16 Oct 2023 13:00:51 +0200 Subject: [PATCH 094/134] bugfix: add multiline comment completion [Cherry-picked 93f863c407f9c7ece40efd89c64dd53eec6d2074] --- .../dotty/tools/pc/completions/Completions.scala | 9 +++++++++ .../completions/MultilineCommentCompletion.scala | 16 ++++++++++++++++ .../pc/tests/completion/CompletionSuite.scala | 14 ++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 presentation-compiler/src/main/dotty/tools/pc/completions/MultilineCommentCompletion.scala diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index 64bbbb848289..aa3fa59e518e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -286,6 +286,15 @@ class Completions( path match case ScalaCliCompletions(dependency) => (ScalaCliCompletions.contribute(dependency), true) + + case _ + if MultilineCommentCompletion.isMultilineCommentCompletion( + pos, + text, + ) => + val values = MultilineCommentCompletion.contribute(config) + (values, true) + case _ if ScaladocCompletions.isScaladocCompletion(pos, text) => val values = ScaladocCompletions.contribute(pos, text, config) (values, true) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MultilineCommentCompletion.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MultilineCommentCompletion.scala new file mode 100644 index 000000000000..46a23446a7f1 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MultilineCommentCompletion.scala @@ -0,0 +1,16 @@ +package dotty.tools.pc.completions + +import scala.meta.pc.PresentationCompilerConfig + +import dotty.tools.dotc.util.SourcePosition + +object MultilineCommentCompletion: + + def contribute(config: PresentationCompilerConfig): List[CompletionValue] = + val newText = if config.isCompletionSnippetsEnabled then " $0 */" else " */" + List(CompletionValue.document("/* */", newText, "Multiline Comment")) + + def isMultilineCommentCompletion(pos: SourcePosition, text: String): Boolean = + pos.point >= 2 && + text.charAt(pos.point - 2) == '/' && + text.charAt(pos.point - 1) == '*' diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 7ca7f9a19b04..213dd7157293 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -1486,3 +1486,17 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, ) + @Test def `multiline-comment` = + checkEdit( + """|package a + |object O: + | /*@@ + | def f = 1 + |""".stripMargin, + """|package a + |object O: + | /* $0 */ + | def f = 1 + |""".stripMargin, + ) + From 56ac7fdef3c88ad78bc7e070b474f6fd3a801773 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 27 Sep 2023 13:57:16 +0100 Subject: [PATCH 095/134] Fix exhaustivity due to separate TypeVar lambdas [Cherry-picked d5a6d4f35dce17ebe4bcc23c0c204c586f042ef7] --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 6 +++--- tests/pos/i14224.1.scala | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i14224.1.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b6bf8c550a57..0165121c9779 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -485,8 +485,8 @@ object SpaceEngine { erase(parent, inArray, isValue, isTyped) case tref: TypeRef if tref.symbol.isPatternBound => - if inArray then tref.underlying - else if isValue then tref.superType + if inArray then erase(tref.underlying, inArray, isValue, isTyped) + else if isValue then erase(tref.superType, inArray, isValue, isTyped) else WildcardType case _ => tp @@ -540,7 +540,7 @@ object SpaceEngine { val mt: MethodType = unapp.widen match { case mt: MethodType => mt case pt: PolyType => - val tvars = pt.paramInfos.map(newTypeVar(_)) + val tvars = constrained(pt, EmptyTree)._2.tpes val mt = pt.instantiate(tvars).asInstanceOf[MethodType] scrutineeTp <:< mt.paramInfos(0) // force type inference to infer a narrower type: could be singleton diff --git a/tests/pos/i14224.1.scala b/tests/pos/i14224.1.scala new file mode 100644 index 000000000000..c0eaa2eedbcd --- /dev/null +++ b/tests/pos/i14224.1.scala @@ -0,0 +1,11 @@ +//> using options -Werror + +// Derived from the extensive test in the gist in i14224 +// Minimising to the false positive in SealedTrait1.either + +sealed trait Foo[A, A1 <: A] +final case class Bar[A, A1 <: A](value: A1) extends Foo[A, A1] + +class Main: + def test[A, A1 <: A](foo: Foo[A, A1]): A1 = foo match + case Bar(v) => v From fe3c152c53e9de08037e44257255a0fa9a8f2ec6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 28 Sep 2023 11:30:32 +0100 Subject: [PATCH 096/134] Rework ProtoType's constrained API Remove the single use overload, replace with a much more used alternative Also return TypeVars instead of TypeTrees, so we don't have to unwrap the useless wrapper a bunch of times, and instead we wrap the few times we really do want to. [Cherry-picked 5b57e09d8bdd6f5a7c5f0b23a6f6e450bca3e90b] --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 5 ++-- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 3 ++ .../dotc/transform/SyntheticMembers.scala | 9 ++---- .../tools/dotc/transform/TypeTestsCasts.scala | 2 +- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../dotty/tools/dotc/typer/Inferencing.scala | 2 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 30 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- compiler/test/dotty/tools/SignatureTest.scala | 4 +-- .../tools/dotc/core/ConstraintsTest.scala | 19 +++++------- 11 files changed, 37 insertions(+), 43 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 590747738431..4884888a8959 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -759,7 +759,7 @@ object Trees { /** A type tree that represents an existing or inferred type */ case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] with TypTree[T] { - type ThisTree[+T <: Untyped] = TypeTree[T] + type ThisTree[+T <: Untyped] <: TypeTree[T] override def isEmpty: Boolean = !hasType override def toString: String = s"TypeTree${if (hasType) s"[$typeOpt]" else ""}" @@ -783,7 +783,8 @@ object Trees { * - as a (result-)type of an inferred ValDef or DefDef. * Every TypeVar is created as the type of one InferredTypeTree. */ - class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T] + class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]: + type ThisTree[+T <: Untyped] <: InferredTypeTree[T] /** ref.type */ case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 1e86c5039c7d..2dc938a581a1 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3125,7 +3125,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { def matchCase(cas: Type): MatchResult = trace(i"$scrut match ${MatchTypeTrace.caseText(cas)}", matchTypes, show = true) { val cas1 = cas match { case cas: HKTypeLambda => - caseLambda = constrained(cas) + caseLambda = constrained(cas, ast.tpd.EmptyTree)._1 caseLambda.resultType case _ => cas diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 545bb138969a..c3e15ea86a4b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4885,6 +4885,9 @@ object Types { if (inst.exists) inst else origin } + def wrapInTypeTree(owningTree: Tree)(using Context): InferredTypeTree = + new InferredTypeTree().withSpan(owningTree.span).withType(this) + override def computeHash(bs: Binders): Int = identityHash(bs) override def equals(that: Any): Boolean = this.eq(that.asInstanceOf[AnyRef]) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 1560ae6e5618..b42fd93319f5 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -501,12 +501,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) { (rawRef, rawInfo) baseInfo match case tl: PolyType => - val (tl1, tpts) = constrained(tl, untpd.EmptyTree, alwaysAddTypeVars = true) - val targs = - for (tpt <- tpts) yield - tpt.tpe match { - case tvar: TypeVar => tvar.instantiate(fromBelow = false) - } + val tvars = constrained(tl) + val targs = for tvar <- tvars yield + tvar.instantiate(fromBelow = false) (baseRef.appliedTo(targs), extractParams(tl.instantiate(targs))) case methTpe => (baseRef, extractParams(methTpe)) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index f682b54ae731..2c552eec6d12 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -82,7 +82,7 @@ object TypeTestsCasts { case tp: TypeProxy => underlyingLambda(tp.superType) } val typeLambda = underlyingLambda(tycon) - val tvars = constrained(typeLambda, untpd.EmptyTree, alwaysAddTypeVars = true)._2.map(_.tpe) + val tvars = constrained(typeLambda) val P1 = tycon.appliedTo(tvars) debug.println("before " + ctx.typerState.constraint.show) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0165121c9779..b17216d19b03 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -540,7 +540,7 @@ object SpaceEngine { val mt: MethodType = unapp.widen match { case mt: MethodType => mt case pt: PolyType => - val tvars = constrained(pt, EmptyTree)._2.tpes + val tvars = constrained(pt) val mt = pt.instantiate(tvars).asInstanceOf[MethodType] scrutineeTp <:< mt.paramInfos(0) // force type inference to infer a narrower type: could be singleton diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 080048a9e91e..bcb33dc60956 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -317,7 +317,7 @@ object Inferencing { def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match case tl: TypeLambda => val (tl1, tvars) = constrained(tl, tree) - var tree1 = AppliedTypeTree(tree.withType(tl1), tvars) + val tree1 = AppliedTypeTree(tree.withType(tl1), tvars.map(_.wrapInTypeTree(tree))) tree1.tpe <:< pt if isFullyDefined(tree1.tpe, force = ForceDegree.failBottom) then tree1 diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 31cca0301d7f..f190c5b011ed 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -713,7 +713,7 @@ object ProtoTypes { tl: TypeLambda, owningTree: untpd.Tree, alwaysAddTypeVars: Boolean, nestingLevel: Int = ctx.nestingLevel - ): (TypeLambda, List[TypeTree]) = { + ): (TypeLambda, List[TypeVar]) = { val state = ctx.typerState val addTypeVars = alwaysAddTypeVars || !owningTree.isEmpty if (tl.isInstanceOf[PolyType]) @@ -721,33 +721,31 @@ object ProtoTypes { s"inconsistent: no typevars were added to committable constraint ${state.constraint}") // hk type lambdas can be added to constraints without typevars during match reduction - def newTypeVars(tl: TypeLambda): List[TypeTree] = - for (paramRef <- tl.paramRefs) - yield { - val tt = InferredTypeTree().withSpan(owningTree.span) + def newTypeVars(tl: TypeLambda): List[TypeVar] = + for paramRef <- tl.paramRefs + yield val tvar = TypeVar(paramRef, state, nestingLevel) state.ownedVars += tvar - tt.withType(tvar) - } + tvar val added = state.constraint.ensureFresh(tl) - val tvars = if (addTypeVars) newTypeVars(added) else Nil - TypeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]]) + val tvars = if addTypeVars then newTypeVars(added) else Nil + TypeComparer.addToConstraint(added, tvars) (added, tvars) } - def constrained(tl: TypeLambda, owningTree: untpd.Tree)(using Context): (TypeLambda, List[TypeTree]) = + def constrained(tl: TypeLambda, owningTree: untpd.Tree)(using Context): (TypeLambda, List[TypeVar]) = constrained(tl, owningTree, alwaysAddTypeVars = tl.isInstanceOf[PolyType] && ctx.typerState.isCommittable) - /** Same as `constrained(tl, EmptyTree)`, but returns just the created type lambda */ - def constrained(tl: TypeLambda)(using Context): TypeLambda = - constrained(tl, EmptyTree)._1 + /** Same as `constrained(tl, EmptyTree, alwaysAddTypeVars = true)`, but returns just the created type vars. */ + def constrained(tl: TypeLambda)(using Context): List[TypeVar] = + constrained(tl, EmptyTree, alwaysAddTypeVars = true)._2 /** Instantiate `tl` with fresh type variables added to the constraint. */ def instantiateWithTypeVars(tl: TypeLambda)(using Context): Type = - val targs = constrained(tl, ast.tpd.EmptyTree, alwaysAddTypeVars = true)._2 - tl.instantiate(targs.tpes) + val tvars = constrained(tl) + tl.instantiate(tvars) /** A fresh type variable added to the current constraint. * @param bounds The initial bounds of the variable @@ -766,7 +764,7 @@ object ProtoTypes { pt => bounds :: Nil, pt => represents.orElse(defn.AnyType)) constrained(poly, untpd.EmptyTree, alwaysAddTypeVars = true, nestingLevel) - ._2.head.tpe.asInstanceOf[TypeVar] + ._2.head /** If `param` was created using `newTypeVar(..., represents = X)`, returns X. * This is used in: diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 13544c32cac2..982895294cf0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4260,7 +4260,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer var typeArgs = tree match case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree(_)) case _ => Nil - if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2 + if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2.map(_.wrapInTypeTree(tree)) convertNewGenericArray(readapt(tree.appliedToTypeTrees(typeArgs))) case wtp => val isStructuralCall = wtp.isValueType && isStructuralTermSelectOrApply(tree) diff --git a/compiler/test/dotty/tools/SignatureTest.scala b/compiler/test/dotty/tools/SignatureTest.scala index fdbb1e8f3760..587d7098a0a7 100644 --- a/compiler/test/dotty/tools/SignatureTest.scala +++ b/compiler/test/dotty/tools/SignatureTest.scala @@ -63,7 +63,7 @@ class SignatureTest: | def tuple2(x: Foo *: (T | Tuple) & Foo): Unit = {} |""".stripMargin): val cls = requiredClass("A") - val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda], untpd.EmptyTree, alwaysAddTypeVars = true)._2.head.tpe + val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda]).head tvar <:< defn.TupleTypeRef val prefix = cls.typeRef.appliedTo(tvar) @@ -89,7 +89,7 @@ class SignatureTest: | def and(x: T & Foo): Unit = {} |""".stripMargin): val cls = requiredClass("A") - val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda], untpd.EmptyTree, alwaysAddTypeVars = true)._2.head.tpe + val tvar = constrained(cls.requiredMethod(nme.CONSTRUCTOR).info.asInstanceOf[TypeLambda]).head val prefix = cls.typeRef.appliedTo(tvar) val ref = prefix.select(cls.requiredMethod("and")).asInstanceOf[TermRef] diff --git a/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala b/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala index 9ae3fda8c6b9..4ca8e243dc0c 100644 --- a/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala +++ b/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala @@ -19,8 +19,7 @@ class ConstraintsTest: @Test def mergeParamsTransitivity: Unit = inCompilerContext(TestConfiguration.basicClasspath, scalaSources = "trait A { def foo[S, T, R]: Any }") { - val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2 - val List(s, t, r) = tvars.tpes + val List(s, t, r) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda]) val innerCtx = ctx.fresh.setExploreTyperState() inContext(innerCtx) { @@ -38,8 +37,7 @@ class ConstraintsTest: @Test def mergeBoundsTransitivity: Unit = inCompilerContext(TestConfiguration.basicClasspath, scalaSources = "trait A { def foo[S, T]: Any }") { - val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2 - val List(s, t) = tvars.tpes + val List(s, t) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda]) val innerCtx = ctx.fresh.setExploreTyperState() inContext(innerCtx) { @@ -57,10 +55,9 @@ class ConstraintsTest: @Test def validBoundsInit: Unit = inCompilerContext( TestConfiguration.basicClasspath, scalaSources = "trait A { def foo[S >: T <: T | Int, T <: String]: Any }") { - val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2 - val List(s, t) = tvars.tpes + val List(s, t) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda]) - val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.asInstanceOf[TypeVar].origin): @unchecked + val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.origin): @unchecked assert(lo =:= defn.NothingType, i"Unexpected lower bound $lo for $t: ${ctx.typerState.constraint}") assert(hi =:= defn.StringType, i"Unexpected upper bound $hi for $t: ${ctx.typerState.constraint}") // used to be Any } @@ -68,12 +65,11 @@ class ConstraintsTest: @Test def validBoundsUnify: Unit = inCompilerContext( TestConfiguration.basicClasspath, scalaSources = "trait A { def foo[S >: T <: T | Int, T <: String | Int]: Any }") { - val tvars = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2 - val List(s, t) = tvars.tpes + val List(s, t) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda]) s <:< t - val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.asInstanceOf[TypeVar].origin): @unchecked + val TypeBounds(lo, hi) = ctx.typerState.constraint.entry(t.origin): @unchecked assert(lo =:= defn.NothingType, i"Unexpected lower bound $lo for $t: ${ctx.typerState.constraint}") assert(hi =:= (defn.StringType | defn.IntType), i"Unexpected upper bound $hi for $t: ${ctx.typerState.constraint}") } @@ -81,8 +77,7 @@ class ConstraintsTest: @Test def validBoundsReplace: Unit = inCompilerContext( TestConfiguration.basicClasspath, scalaSources = "trait X; trait A { def foo[S <: U | X, T, U]: Any }") { - val tvarTrees = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda], EmptyTree, alwaysAddTypeVars = true)._2 - val tvars @ List(s, t, u) = tvarTrees.tpes.asInstanceOf[List[TypeVar]] + val tvars @ List(s, t, u) = constrained(requiredClass("A").typeRef.select("foo".toTermName).info.asInstanceOf[TypeLambda]) s =:= t t =:= u From 2466f16f6613caac430bfd9917431b01a4be4dbc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 3 Oct 2023 17:05:41 +0100 Subject: [PATCH 097/134] Consider extension methods in Space isSameUnapply [Cherry-picked 5a8a0ed134aaf4acd2e9a3ec2e9269371a8a2460] --- .../tools/dotc/transform/patmat/Space.scala | 8 ++++-- tests/pos/i18601.scala | 19 ++++++++++++++ tests/pos/i18601b.scala | 26 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i18601.scala create mode 100644 tests/pos/i18601b.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b17216d19b03..1dbbd5d15202 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -525,10 +525,14 @@ object SpaceEngine { * We assume that unapply methods are pure, but the same method may * be called with different prefixes, thus behaving differently. */ - def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean = + def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean = trace(i"isSameUnapply($tp1, $tp2)") { + def isStable(tp: TermRef) = + !tp.symbol.is(ExtensionMethod) // The "prefix" of an extension method may be, but the receiver isn't, so exclude + && tp.prefix.isStable // always assume two TypeTest[S, T].unapply are the same if they are equal in types - (tp1.prefix.isStable && tp2.prefix.isStable || tp1.symbol == defn.TypeTest_unapply) + (isStable(tp1) && isStable(tp2) || tp1.symbol == defn.TypeTest_unapply) && tp1 =:= tp2 + } /** Return term parameter types of the extractor `unapp`. * Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */ diff --git a/tests/pos/i18601.scala b/tests/pos/i18601.scala new file mode 100644 index 000000000000..63468e2d8c32 --- /dev/null +++ b/tests/pos/i18601.scala @@ -0,0 +1,19 @@ +//> using options -Werror +extension (sc: StringContext) + def m: StringContext = sc + def unapply(string: String): Option[String] = + val pattern = sc.parts.head + if string.length == pattern.length then Some(string) else None + +class Test: + def parse(x: PartialFunction[String, String]) = x + + val pf = parse { + case m"x$s" => s + case m"xx$s" => s // was: unreachable + } + + // proof that the second case isn't unreachable (matches "ab") + def t1 = pf.applyOrElse("a", _ => ".") // "a" + def t2 = pf.applyOrElse("ab", _ => ".") // "ab" + def t3 = pf.applyOrElse("abc", _ => ".") // "." diff --git a/tests/pos/i18601b.scala b/tests/pos/i18601b.scala new file mode 100644 index 000000000000..5646b909bd67 --- /dev/null +++ b/tests/pos/i18601b.scala @@ -0,0 +1,26 @@ +//> using options -Werror + +// like pos/i18601 +// but with a dedicated SC class +// that made the false positive redundancy warning go away + +extension (sc: StringContext) + def m: SC = SC(sc) + +class SC(sc: StringContext): + def unapply(string: String): Option[String] = + val pattern = sc.parts.head + if string.length == pattern.length then Some(string) else None + +class Test: + def parse(x: PartialFunction[String, String]) = x + + val pf = parse { + case m"x$s" => s + case m"xx$s" => s // was: not unreachable (as a counter-example) + } + + // proof that the second case isn't unreachable (matches "ab") + def t1 = pf.applyOrElse("a", _ => ".") // "a" + def t2 = pf.applyOrElse("ab", _ => ".") // "ab" + def t3 = pf.applyOrElse("abc", _ => ".") // "." From c86d40be07090a0f9330362dda52159af93c8281 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 13 Oct 2023 15:16:00 +0200 Subject: [PATCH 098/134] Refactor CannotBeAccessed/isAccessibleFrom [Cherry-picked 8aec15b3e3072dac90b9b08ae7614929ef22586a] --- .../tools/dotc/core/SymDenotations.scala | 21 ++++--------------- .../dotty/tools/dotc/reporting/messages.scala | 10 +++++++-- tests/neg/i18686.check | 13 ++++++++++++ tests/neg/i18686.scala | 13 ++++++++++++ 4 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 tests/neg/i18686.check create mode 100644 tests/neg/i18686.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index cad15c96dcae..33e8743815c9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -877,7 +877,7 @@ object SymDenotations { * As a side effect, drop Local flags of members that are not accessed via the ThisType * of their owner. */ - final def isAccessibleFrom(pre: Type, superAccess: Boolean = false, whyNot: StringBuffer | Null = null)(using Context): Boolean = { + final def isAccessibleFrom(pre: Type, superAccess: Boolean = false)(using Context): Boolean = { /** Are we inside definition of `boundary`? * If this symbol is Java defined, package structure is interpreted to be flat. @@ -906,26 +906,13 @@ object SymDenotations { /** Is protected access to target symbol permitted? */ def isProtectedAccessOK: Boolean = - inline def fail(str: String): false = - if whyNot != null then whyNot.nn.append(str) - false val cls = owner.enclosingSubClass if !cls.exists then - if pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) then - true - else - val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass - val location = if owner.is(Final) then owner.showLocated else owner.showLocated + " or one of its subclasses" - fail(i""" - | Protected $this can only be accessed from $location.""") - else if isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) then + pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) + else // allow accesses to types from arbitrary subclasses fixes #4737 // don't perform this check for static members - true - else - val location = if cls.is(Final) then cls.showLocated else cls.showLocated + " or one of its subclasses" - fail(i""" - | Protected $this can only be accessed from $location.""") + isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) end isProtectedAccessOK if pre eq NoPrefix then true diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index c6ad4648b69a..c682054d344b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -876,7 +876,7 @@ extends Message(PatternMatchExhaustivityID) { val pathes = List( ActionPatch( - srcPos = endPos, + srcPos = endPos, replacement = uncoveredCases.map(c => indent(s"case $c => ???", startColumn)) .mkString("\n", "\n", "") ), @@ -2963,7 +2963,13 @@ extends ReferenceMsg(CannotBeAccessedID): i"none of the overloaded alternatives named $name can" val where = if (ctx.owner.exists) i" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer - alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) + for alt <- alts do + if alt.is(Protected) then + val cls = alt.owner.enclosingSubClass + val owner = if cls.exists then cls else alt.owner + val location = if owner.is(Final) then owner.showLocated else owner.showLocated + " or one of its subclasses" + whyNot.append(i""" + | Protected $alt can only be accessed from $location.""") i"$whatCanNot be accessed as a member of $pre$where.$whyNot" def explain(using Context) = "" diff --git a/tests/neg/i18686.check b/tests/neg/i18686.check new file mode 100644 index 000000000000..cfecb5522248 --- /dev/null +++ b/tests/neg/i18686.check @@ -0,0 +1,13 @@ +-- [E173] Reference Error: tests/neg/i18686.scala:9:16 ----------------------------------------------------------------- +9 | println(Foo.Bar1) // error + | ^^^^^^^^ + | value Bar1 cannot be accessed as a member of Foo.type from object Main. +-- [E173] Reference Error: tests/neg/i18686.scala:10:16 ---------------------------------------------------------------- +10 | println(Foo.Bar2) // error + | ^^^^^^^^ + | value Bar2 cannot be accessed as a member of Foo.type from object Main. +-- [E173] Reference Error: tests/neg/i18686.scala:11:16 ---------------------------------------------------------------- +11 | println(Foo.Bar3) // error + | ^^^^^^^^ + | value Bar3 cannot be accessed as a member of Foo.type from object Main. + | Protected value Bar3 can only be accessed from object Foo. diff --git a/tests/neg/i18686.scala b/tests/neg/i18686.scala new file mode 100644 index 000000000000..d6a45b171394 --- /dev/null +++ b/tests/neg/i18686.scala @@ -0,0 +1,13 @@ +object Foo: + private val Bar1: Int = 3 + private[Foo] val Bar2: Int = 3 + protected val Bar3: Int = 3 +end Foo + +object Main: + def main(args: Array[String]): Unit = + println(Foo.Bar1) // error + println(Foo.Bar2) // error + println(Foo.Bar3) // error + end main +end Main From 4c8e871ac9c5de6ceb380870efd1210de1f1a7b2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 13 Oct 2023 15:34:18 +0200 Subject: [PATCH 099/134] Explain accessible scope of private members in error message Fixes #18686 [Cherry-picked 6c1fce06fcbf1c8742e49dda52f6be032830ef6a] --- community-build/community-projects/stdLib213 | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 23 ++++++++---- compiler/test-resources/repl/i1370 | 1 + tests/neg/i12573.check | 2 +- tests/neg/i14432c.check | 1 + tests/neg/i18686.check | 35 ++++++++++++++----- tests/neg/i18686.scala | 11 ++++-- tests/neg/i18686b.check | 28 +++++++++++++++ tests/neg/i18686b.scala | 22 ++++++++++++ tests/neg/i18686c.check | 8 +++++ tests/neg/i18686c.scala | 8 +++++ tests/neg/i7709.check | 16 ++++----- tests/neg/not-accessible.check | 5 +++ 13 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 tests/neg/i18686b.check create mode 100644 tests/neg/i18686b.scala create mode 100644 tests/neg/i18686c.check create mode 100644 tests/neg/i18686c.scala diff --git a/community-build/community-projects/stdLib213 b/community-build/community-projects/stdLib213 index 88383e58f82c..6243e902928c 160000 --- a/community-build/community-projects/stdLib213 +++ b/community-build/community-projects/stdLib213 @@ -1 +1 @@ -Subproject commit 88383e58f82cd728afc9316081c2350489c39943 +Subproject commit 6243e902928c344fb0e82e21120bb257f08a2af2 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index c682054d344b..84bc55bbd38b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2964,12 +2964,23 @@ extends ReferenceMsg(CannotBeAccessedID): val where = if (ctx.owner.exists) i" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer for alt <- alts do - if alt.is(Protected) then - val cls = alt.owner.enclosingSubClass - val owner = if cls.exists then cls else alt.owner - val location = if owner.is(Final) then owner.showLocated else owner.showLocated + " or one of its subclasses" - whyNot.append(i""" - | Protected $alt can only be accessed from $location.""") + val cls = alt.owner.enclosingSubClass + val owner = if cls.exists then cls else alt.owner + val location: String = + if alt.is(Protected) then + if alt.privateWithin.exists && alt.privateWithin != owner then + if owner.is(Final) then alt.privateWithin.showLocated + else alt.privateWithin.showLocated + ", or " + owner.showLocated + " or one of its subclasses" + else + if owner.is(Final) then owner.showLocated + else owner.showLocated + " or one of its subclasses" + else + alt.privateWithin.orElse(owner).showLocated + val accessMod = if alt.is(Protected) then "protected" else "private" + val within = if alt.privateWithin.exists then i"[${alt.privateWithin.name}]" + else "" + whyNot.append(i""" + | $accessMod$within $alt can only be accessed from $location.""") i"$whatCanNot be accessed as a member of $pre$where.$whyNot" def explain(using Context) = "" diff --git a/compiler/test-resources/repl/i1370 b/compiler/test-resources/repl/i1370 index 4bd92b4d5f83..e10020bf4891 100644 --- a/compiler/test-resources/repl/i1370 +++ b/compiler/test-resources/repl/i1370 @@ -3,4 +3,5 @@ scala> object Lives { class Private { def foo1: Any = new Private.C1; def foo2: 1 | object Lives { class Private { def foo1: Any = new Private.C1; def foo2: Any = new Private.C2 }; object Private { class C1 private {}; private class C2 {} } } | ^^^^^^^^^^ |constructor C1 cannot be accessed as a member of Lives.Private.C1 from class Private. + | private constructor C1 can only be accessed from class C1 in object Private. 1 error found diff --git a/tests/neg/i12573.check b/tests/neg/i12573.check index 8c744fda685b..50fe36aa2aa9 100644 --- a/tests/neg/i12573.check +++ b/tests/neg/i12573.check @@ -5,4 +5,4 @@ |Extension methods were tried, but the search failed with: | | method getDFType cannot be accessed as a member of DFType.type from the top-level definitions in package . - | Protected method getDFType can only be accessed from object DFType. + | protected method getDFType can only be accessed from object DFType. diff --git a/tests/neg/i14432c.check b/tests/neg/i14432c.check index c0f69a1095d7..2710f0dfb3ed 100644 --- a/tests/neg/i14432c.check +++ b/tests/neg/i14432c.check @@ -2,6 +2,7 @@ 12 |class Bar extends example.Foo(23) { // error: cant access private[example] ctor | ^^^^^^^^^^^ | constructor Foo cannot be accessed as a member of example.Foo from class Bar. + | private[example] constructor Foo can only be accessed from package example. -- [E172] Type Error: tests/neg/i14432c.scala:16:43 -------------------------------------------------------------------- 16 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror | ^ diff --git a/tests/neg/i18686.check b/tests/neg/i18686.check index cfecb5522248..6ed69c515051 100644 --- a/tests/neg/i18686.check +++ b/tests/neg/i18686.check @@ -1,13 +1,30 @@ --- [E173] Reference Error: tests/neg/i18686.scala:9:16 ----------------------------------------------------------------- -9 | println(Foo.Bar1) // error - | ^^^^^^^^ - | value Bar1 cannot be accessed as a member of Foo.type from object Main. --- [E173] Reference Error: tests/neg/i18686.scala:10:16 ---------------------------------------------------------------- -10 | println(Foo.Bar2) // error +-- [E173] Reference Error: tests/neg/i18686.scala:13:16 ---------------------------------------------------------------- +13 | println(Foo.Bar1) // error + | ^^^^^^^^ + | value Bar1 cannot be accessed as a member of Foo.type from object Main. + | private value Bar1 can only be accessed from object Foo. +-- [E173] Reference Error: tests/neg/i18686.scala:14:16 ---------------------------------------------------------------- +14 | println(Foo.Bar2) // error | ^^^^^^^^ | value Bar2 cannot be accessed as a member of Foo.type from object Main. --- [E173] Reference Error: tests/neg/i18686.scala:11:16 ---------------------------------------------------------------- -11 | println(Foo.Bar3) // error + | private[Foo] value Bar2 can only be accessed from object Foo. +-- [E173] Reference Error: tests/neg/i18686.scala:15:16 ---------------------------------------------------------------- +15 | println(Foo.Bar3) // error | ^^^^^^^^ | value Bar3 cannot be accessed as a member of Foo.type from object Main. - | Protected value Bar3 can only be accessed from object Foo. + | protected value Bar3 can only be accessed from object Foo. +-- [E173] Reference Error: tests/neg/i18686.scala:16:16 ---------------------------------------------------------------- +16 | println(Foo.Bar4) // error + | ^^^^^^^^ + | value Bar4 cannot be accessed as a member of Foo.type from object Main. + | protected[Foo] value Bar4 can only be accessed from object Foo. +-- [E173] Reference Error: tests/neg/i18686.scala:17:20 ---------------------------------------------------------------- +17 | println(Foo.Baz.Bar5) // error + | ^^^^^^^^^^^^ + | value Bar5 cannot be accessed as a member of Foo.Baz.type from object Main. + | private[Foo] value Bar5 can only be accessed from object Foo. +-- [E173] Reference Error: tests/neg/i18686.scala:18:20 ---------------------------------------------------------------- +18 | println(Foo.Baz.Bar6) // error + | ^^^^^^^^^^^^ + | value Bar6 cannot be accessed as a member of Foo.Baz.type from object Main. + | protected[Foo] value Bar6 can only be accessed from object Foo. diff --git a/tests/neg/i18686.scala b/tests/neg/i18686.scala index d6a45b171394..88da7b9d802a 100644 --- a/tests/neg/i18686.scala +++ b/tests/neg/i18686.scala @@ -1,7 +1,11 @@ object Foo: - private val Bar1: Int = 3 - private[Foo] val Bar2: Int = 3 + private val Bar1: Int = 1 + private[Foo] val Bar2: Int = 2 protected val Bar3: Int = 3 + protected[Foo] val Bar4: Int = 5 + object Baz: + private[Foo] val Bar5: Int = 5 + protected[Foo] val Bar6: Int = 6 end Foo object Main: @@ -9,5 +13,8 @@ object Main: println(Foo.Bar1) // error println(Foo.Bar2) // error println(Foo.Bar3) // error + println(Foo.Bar4) // error + println(Foo.Baz.Bar5) // error + println(Foo.Baz.Bar6) // error end main end Main diff --git a/tests/neg/i18686b.check b/tests/neg/i18686b.check new file mode 100644 index 000000000000..6394aeedf35a --- /dev/null +++ b/tests/neg/i18686b.check @@ -0,0 +1,28 @@ +-- [E173] Reference Error: tests/neg/i18686b.scala:15:16 --------------------------------------------------------------- +15 | println(foo.Bar1) // error + | ^^^^^^^^ + | value Bar1 cannot be accessed as a member of Foo from object Main. + | private value Bar1 can only be accessed from class Foo. +-- [E173] Reference Error: tests/neg/i18686b.scala:16:16 --------------------------------------------------------------- +16 | println(foo.Bar2) // error + | ^^^^^^^^ + | value Bar2 cannot be accessed as a member of Foo from object Main. + | private[Foo] value Bar2 can only be accessed from class Foo. +-- [E173] Reference Error: tests/neg/i18686b.scala:17:16 --------------------------------------------------------------- +17 | println(foo.Bar3) // error + | ^^^^^^^^ + | value Bar3 cannot be accessed as a member of Foo from object Main. + | protected value Bar3 can only be accessed from class Foo or one of its subclasses. +-- [E173] Reference Error: tests/neg/i18686b.scala:18:16 --------------------------------------------------------------- +18 | println(foo.Bar4) // error + | ^^^^^^^^ + | value Bar4 cannot be accessed as a member of Foo from object Main. + | protected[Foo] value Bar4 can only be accessed from class Foo or one of its subclasses. +-- [E008] Not Found Error: tests/neg/i18686b.scala:19:20 --------------------------------------------------------------- +19 | println(foo.Baz.Bar5) // error + | ^^^^^^^^^^^^ + | value Bar5 is not a member of object Foo#Baz +-- [E008] Not Found Error: tests/neg/i18686b.scala:20:20 --------------------------------------------------------------- +20 | println(foo.Baz.Bar6) // error + | ^^^^^^^^^^^^ + | value Bar6 is not a member of object Foo#Baz diff --git a/tests/neg/i18686b.scala b/tests/neg/i18686b.scala new file mode 100644 index 000000000000..86f8066073c0 --- /dev/null +++ b/tests/neg/i18686b.scala @@ -0,0 +1,22 @@ +class Foo: + private val Bar1: Int = 1 + private[Foo] val Bar2: Int = 2 + protected val Bar3: Int = 3 + protected[Foo] val Bar4: Int = 5 + class Baz: + private[Foo] val Bar5: Int = 5 + protected[Foo] val Bar6: Int = 6 +end Foo + +def foo = new Foo + +object Main: + def main(args: Array[String]): Unit = + println(foo.Bar1) // error + println(foo.Bar2) // error + println(foo.Bar3) // error + println(foo.Bar4) // error + println(foo.Baz.Bar5) // error + println(foo.Baz.Bar6) // error + end main +end Main diff --git a/tests/neg/i18686c.check b/tests/neg/i18686c.check new file mode 100644 index 000000000000..328d9cfd42a6 --- /dev/null +++ b/tests/neg/i18686c.check @@ -0,0 +1,8 @@ +-- [E173] Reference Error: tests/neg/i18686c.scala:8:6 ----------------------------------------------------------------- +8 | foo.foo // error + | ^^^^^^^ + |method foo cannot be accessed as a member of (foo² : Bar.Foo) from the top-level definitions in package . + | protected[Bar] method foo can only be accessed from object Bar, or class Foo in object Bar or one of its subclasses. + | + |where: foo is a method in class Foo + | foo² is a parameter in method test diff --git a/tests/neg/i18686c.scala b/tests/neg/i18686c.scala new file mode 100644 index 000000000000..d120e416ed9f --- /dev/null +++ b/tests/neg/i18686c.scala @@ -0,0 +1,8 @@ +object Bar: + class Foo: + protected[Bar] def foo = 23 + class Qux extends Foo: + val qux = foo + +def test(foo: Bar.Foo) = + foo.foo // error diff --git a/tests/neg/i7709.check b/tests/neg/i7709.check index b3b4e21b9db9..14d2dbaf4cde 100644 --- a/tests/neg/i7709.check +++ b/tests/neg/i7709.check @@ -2,39 +2,39 @@ 5 | class B extends X.Y // error | ^^^ | class Y cannot be accessed as a member of X.type from class B. - | Protected class Y can only be accessed from object X. + | protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:6:21 ------------------------------------------------------------------ 6 | class B2 extends X.Y: // error | ^^^ | class Y cannot be accessed as a member of X.type from class B2. - | Protected class Y can only be accessed from object X. + | protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:9:28 ------------------------------------------------------------------ 9 | class B4 extends B3(new X.Y) // error | ^^^ | class Y cannot be accessed as a member of X.type from class B4. - | Protected class Y can only be accessed from object X. + | protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:11:34 ----------------------------------------------------------------- 11 | def this(n: Int) = this(new X.Y().toString) // error | ^^^ | class Y cannot be accessed as a member of X.type from class B5. - | Protected class Y can only be accessed from object X. + | protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:13:20 ----------------------------------------------------------------- 13 | class B extends X.Y // error | ^^^ | class Y cannot be accessed as a member of X.type from class B. - | Protected class Y can only be accessed from object X. + | protected class Y can only be accessed from object X. -- [E173] Reference Error: tests/neg/i7709.scala:18:18 ----------------------------------------------------------------- 18 | def y = new xx.Y // error | ^^^^ | class Y cannot be accessed as a member of XX from class C. - | Protected class Y can only be accessed from class XX or one of its subclasses. + | protected class Y can only be accessed from class XX or one of its subclasses. -- [E173] Reference Error: tests/neg/i7709.scala:23:20 ----------------------------------------------------------------- 23 | def y = new xx.Y // error | ^^^^ | class Y cannot be accessed as a member of XX from class D. - | Protected class Y can only be accessed from class XX or one of its subclasses. + | protected class Y can only be accessed from class XX or one of its subclasses. -- [E173] Reference Error: tests/neg/i7709.scala:31:20 ----------------------------------------------------------------- 31 | class Q extends X.Y // error | ^^^ | class Y cannot be accessed as a member of p.X.type from class Q. - | Protected class Y can only be accessed from object X in package p. + | protected class Y can only be accessed from object X in package p. diff --git a/tests/neg/not-accessible.check b/tests/neg/not-accessible.check index 00206d281016..54585460a1d8 100644 --- a/tests/neg/not-accessible.check +++ b/tests/neg/not-accessible.check @@ -2,19 +2,24 @@ 8 | def test(a: A) = a.x // error | ^^^ | value x cannot be accessed as a member of (a : foo.A) from class B. + | private[A] value x can only be accessed from class A in package foo. -- [E173] Reference Error: tests/neg/not-accessible.scala:10:23 -------------------------------------------------------- 10 | def test(a: A) = a.x // error | ^^^ | value x cannot be accessed as a member of (a : foo.A) from object B. + | private[A] value x can only be accessed from class A in package foo. -- [E173] Reference Error: tests/neg/not-accessible.scala:13:23 -------------------------------------------------------- 13 | def test(a: A) = a.x // error | ^^^ | value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package bar. + | private[A] value x can only be accessed from class A in package foo. -- [E173] Reference Error: tests/neg/not-accessible.scala:5:21 --------------------------------------------------------- 5 | def test(a: A) = a.x // error | ^^^ | value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package foo. + | private[A] value x can only be accessed from class A in package foo. -- [E173] Reference Error: tests/neg/not-accessible.scala:15:23 -------------------------------------------------------- 15 |def test(a: foo.A) = a.x // error | ^^^ | value x cannot be accessed as a member of (a : foo.A) from the top-level definitions in package . + | private[A] value x can only be accessed from class A in package foo. From dec2e9c79e4a0580956c94532d6510452794a00f Mon Sep 17 00:00:00 2001 From: Pascal Weisenburger Date: Sun, 16 Jul 2023 01:10:30 +0200 Subject: [PATCH 100/134] Make check flags for `newMethod`, `newVal` and `newBind` in Quotes API less restrictive [Cherry-picked 2cf2b28130d3cb087e791245d3370f9c6780e012] --- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 6 +++--- library/src/scala/quoted/Quotes.scala | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index b32d7ff92b81..3f8c404ef5ff 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2879,11 +2879,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def Transparent: Flags = dotc.core.Flags.Transparent // Keep: aligned with Quotes's `newMethod` doc - private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | JavaStatic // Flags that could be allowed: Synthetic | ExtensionMethod | Exported | Erased | Infix | Invisible + private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | Synthetic | Artifact | ExtensionMethod | Exported | Erased | Infix | Invisible | JavaStatic // Keep: aligned with Quotes's `newVal` doc - private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | JavaStatic // Flags that could be added: Synthetic | Erased | Invisible + private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | Synthetic | Artifact | Erased | Invisible | JavaStatic // Keep: aligned with Quotes's `newBind` doc - private[QuotesImpl] def validBindFlags: Flags = Case // Flags that could be allowed: Implicit | Given | Erased + private[QuotesImpl] def validBindFlags: Flags = Case | Implicit | Given | Erased end Flags given FlagsMethods: FlagsMethods with diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index b6e5a12da2d8..41d173a574e5 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3785,7 +3785,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the method * @param name The name of the method * @param tpe The type of the method (MethodType, PolyType, ByNameType) - * @param flags extra flags to with which the symbol should be constructed. `Method` flag will be added. Can be `Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | JavaStatic` + * @param flags extra flags to with which the symbol should be constructed. `Method` flag will be added. Can be `Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | Synthetic | Artifact | ExtensionMethod | Exported | Erased | Infix | Invisible | JavaStatic` * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. */ // Keep: `flags` doc aligned with QuotesImpl's `validMethodFlags` @@ -3802,7 +3802,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the val/var/lazy val * @param name The name of the val/var/lazy val * @param tpe The type of the val/var/lazy val - * @param flags extra flags to with which the symbol should be constructed. Can be `Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | JavaStatic` + * @param flags extra flags to with which the symbol should be constructed. Can be `Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | Synthetic | Artifact | Erased | Invisible | JavaStatic` * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. @@ -3818,7 +3818,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * * @param parent The owner of the binding * @param name The name of the binding - * @param flags extra flags to with which the symbol should be constructed. `Case` flag will be added. Can be `Case` + * @param flags extra flags to with which the symbol should be constructed. `Case` flag will be added. Can be `Case | Implicit | Given | Erased` * @param tpe The type of the binding * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. From c0ef901f956ac9e3795d81394fb602820dddc827 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 3 Aug 2023 15:10:50 +0200 Subject: [PATCH 101/134] Apply suggestions from code review [Cherry-picked 510c6bae8f46261a395b0f27751b5c6052542a15] --- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 7 ++++--- library/src/scala/quoted/Quotes.scala | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 3f8c404ef5ff..a20e6ef73c80 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2879,11 +2879,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def Transparent: Flags = dotc.core.Flags.Transparent // Keep: aligned with Quotes's `newMethod` doc - private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | Synthetic | Artifact | ExtensionMethod | Exported | Erased | Infix | Invisible | JavaStatic + private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | JavaStatic | Synthetic | Artifact // Flags that could be allowed: Synthetic | ExtensionMethod | Exported | Erased | Infix | Invisible // Keep: aligned with Quotes's `newVal` doc - private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | Synthetic | Artifact | Erased | Invisible | JavaStatic + private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | JavaStatic | Synthetic | Artifact // Flags that could be added: Synthetic | Erased | Invisible + // Keep: aligned with Quotes's `newBind` doc - private[QuotesImpl] def validBindFlags: Flags = Case | Implicit | Given | Erased + private[QuotesImpl] def validBindFlags: Flags = Case // Flags that could be allowed: Implicit | Given | Erased end Flags given FlagsMethods: FlagsMethods with diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 41d173a574e5..029a276a75d5 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3785,7 +3785,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the method * @param name The name of the method * @param tpe The type of the method (MethodType, PolyType, ByNameType) - * @param flags extra flags to with which the symbol should be constructed. `Method` flag will be added. Can be `Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | Synthetic | Artifact | ExtensionMethod | Exported | Erased | Infix | Invisible | JavaStatic` + * @param flags extra flags to with which the symbol should be constructed. `Method` flag will be added. Can be `Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | JavaStatic | Synthetic | Artifact` * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. */ // Keep: `flags` doc aligned with QuotesImpl's `validMethodFlags` @@ -3802,7 +3802,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the val/var/lazy val * @param name The name of the val/var/lazy val * @param tpe The type of the val/var/lazy val - * @param flags extra flags to with which the symbol should be constructed. Can be `Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | Synthetic | Artifact | Erased | Invisible | JavaStatic` + * @param flags extra flags to with which the symbol should be constructed. Can be `Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | JavaStatic | Synthetic | Artifact` * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. @@ -3818,7 +3818,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * * @param parent The owner of the binding * @param name The name of the binding - * @param flags extra flags to with which the symbol should be constructed. `Case` flag will be added. Can be `Case | Implicit | Given | Erased` + * @param flags extra flags to with which the symbol should be constructed. `Case` flag will be added. Can be `Case` * @param tpe The type of the binding * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. From 76694076a76766e84c31bb65c0914d358186360b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 17 Oct 2023 14:40:42 +0200 Subject: [PATCH 102/134] Fix import suggestion error logging This was printing the extra debug information when testing tests/neg-deep-subtype/i1650.scala [Cherry-picked 322f87474cf2a4e8980ff9e8b5e0eff8bb10be2e] --- compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index a21a94aab271..f357b972800a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -195,7 +195,7 @@ trait ImportSuggestions: && { val task = new TimerTask: def run() = - println(i"Cancelling test of $ref when making suggestions for error in ${ctx.source}") + implicits.println(i"Cancelling test of $ref when making suggestions for error in ${ctx.source}") ctx.run.nn.isCancelled = true val span = ctx.owner.srcPos.span val (expectedType, argument, kind) = pt match From 2046d25cc8e57c825aad97fbf4c1fc092d7f6955 Mon Sep 17 00:00:00 2001 From: philippus Date: Wed, 18 Oct 2023 07:42:18 +0200 Subject: [PATCH 103/134] Update asm to 9.6 [Cherry-picked 71c8573807444ced478cbcda00aae4244fb658c0] --- compiler/src/dotty/tools/backend/jvm/BackendUtils.scala | 1 + compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- project/Build.scala | 2 +- tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala | 3 ++- tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index 2eaaccdd441d..30919605c584 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -36,6 +36,7 @@ class BackendUtils(val postProcessor: PostProcessor) { case "19" => asm.Opcodes.V19 case "20" => asm.Opcodes.V20 case "21" => asm.Opcodes.V21 + case "22" => asm.Opcodes.V22 } lazy val extraProc: Int = { diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 68671755df37..c07e9b0e5eb0 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -18,7 +18,7 @@ class ScalaSettings extends SettingGroup with AllScalaSettings object ScalaSettings: // Keep synchronized with `classfileVersion` in `BackendUtils` private val minTargetVersion = 8 - private val maxTargetVersion = 21 + private val maxTargetVersion = 22 def supportedTargetVersions: List[String] = (minTargetVersion to maxTargetVersion).toList.map(_.toString) diff --git a/project/Build.scala b/project/Build.scala index d12ab07d31d5..cdbdbf07a846 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -577,7 +577,7 @@ object Build { // get libraries onboard libraryDependencies ++= Seq( - "org.scala-lang.modules" % "scala-asm" % "9.5.0-scala-1", // used by the backend + "org.scala-lang.modules" % "scala-asm" % "9.6.0-scala-1", // used by the backend Dependencies.compilerInterface, "org.jline" % "jline-reader" % "3.19.0", // used by the REPL "org.jline" % "jline-terminal" % "3.19.0", diff --git a/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala b/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala index 5eb8d7a52aa2..2d61f05e40de 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala @@ -55,7 +55,8 @@ trait BCodeIdiomatic extends caps.Pure { case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 case "20" => asm.Opcodes.V20 - case "21" => asm.Opcodes.V21*/ + case "21" => asm.Opcodes.V21 + case "22" => asm.Opcodes.V22*/ } lazy val majorVersion: Int = (classfileVersion & 0xFF) diff --git a/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala b/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala index 20708b98cc95..b3250eb7b536 100644 --- a/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala +++ b/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala @@ -17,7 +17,7 @@ class ScalaSettings extends SettingGroup with AllScalaSettings object ScalaSettings: // Keep synchronized with `classfileVersion` in `BCodeIdiomatic` private val minTargetVersion = 8 - private val maxTargetVersion = 21 + private val maxTargetVersion = 22 def supportedTargetVersions: List[String] = (minTargetVersion to maxTargetVersion).toList.map(_.toString) From a0851f66cb90f46a8759b55d8e3e264d1ee85c1f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 24 Aug 2023 14:43:12 +0200 Subject: [PATCH 104/134] Elide companion defs to a `object` extending `AnyVal` Co-authored-by: Matt Bovel [Cherry-picked bbeb0ab2dd68420a1f732df6f373b4f83b31801a] --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- .../src/dotty/tools/dotc/reporting/messages.scala | 13 +++++++++---- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 ++ tests/neg/i18274.check | 9 +++++++++ tests/neg/i18274.scala | 3 +++ 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 tests/neg/i18274.check create mode 100644 tests/neg/i18274.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 3fcca3b73d6c..3f58e78ff7b1 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -770,7 +770,7 @@ object desugar { } else if (companionMembers.nonEmpty || companionDerived.nonEmpty || isEnum) companionDefs(anyRef, companionMembers) - else if (isValueClass) + else if isValueClass && !isObject then companionDefs(anyRef, Nil) else Nil diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 84bc55bbd38b..61840e2dc6d0 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1670,10 +1670,15 @@ class CannotExtendAnyVal(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendAnyValID) { def msg(using Context) = i"""$sym cannot extend ${hl("AnyVal")}""" def explain(using Context) = - i"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend - |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. - |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. - |""" + if sym.is(Trait) then + i"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend + |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. + |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. + |""" + else if sym.is(Module) then + i"""Only classes (not objects) are allowed to extend ${hl("AnyVal")}. + |""" + else "" } class CannotExtendJavaEnum(sym: Symbol)(using Context) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 3948fecb2a0e..b95bdaf54165 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -701,6 +701,8 @@ object Checking { if (isDerivedValueClass(clazz)) { if (clazz.is(Trait)) report.error(CannotExtendAnyVal(clazz), clazz.srcPos) + if clazz.is(Module) then + report.error(CannotExtendAnyVal(clazz), clazz.srcPos) if (clazz.is(Abstract)) report.error(ValueClassesMayNotBeAbstract(clazz), clazz.srcPos) if (!clazz.isStatic) diff --git a/tests/neg/i18274.check b/tests/neg/i18274.check new file mode 100644 index 000000000000..2535d641451c --- /dev/null +++ b/tests/neg/i18274.check @@ -0,0 +1,9 @@ +-- [E068] Syntax Error: tests/neg/i18274.scala:3:7 --------------------------------------------------------------------- +3 |object Foo extends AnyVal // error + | ^ + | object Foo cannot extend AnyVal + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Only classes (not objects) are allowed to extend AnyVal. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i18274.scala b/tests/neg/i18274.scala new file mode 100644 index 000000000000..19c27b969d98 --- /dev/null +++ b/tests/neg/i18274.scala @@ -0,0 +1,3 @@ +//> using options -explain + +object Foo extends AnyVal // error From 240b1aefc87e14809240d39c6dfec466d0de6700 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 18 Oct 2023 14:48:32 +0200 Subject: [PATCH 105/134] Don't emit line number for synthetic unit value Synthetic `()` values are added to blocks without an expression. Don't emit a line number for them. Implemented by checking the `SyntheticUnit` attachment. This seems simpler than trying to control the position assigned to synthetic unit trees, as they are created in many places. [Cherry-picked e204e30bd68ac109ab96254f319ee0797dd01373] --- .../tools/backend/jvm/BCodeSkelBuilder.scala | 45 +++++++++---------- .../src/dotty/tools/dotc/ast/Desugar.scala | 4 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 + compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../dotty/tools/dotc/inlines/Inliner.scala | 2 +- .../dotty/tools/dotc/inlines/Inlines.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 8 ++-- .../tools/dotc/transform/DropBreaks.scala | 2 +- .../dotty/tools/dotc/transform/Erasure.scala | 2 +- .../dotty/tools/dotc/transform/Memoize.scala | 2 +- .../tools/dotc/transform/PatternMatcher.scala | 2 +- .../tools/dotc/transform/TypeTestsCasts.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../backend/jvm/DottyBytecodeTests.scala | 19 ++++++++ 16 files changed, 60 insertions(+), 40 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 70d4d758d83b..073cc44e76b7 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -3,26 +3,24 @@ package backend package jvm import scala.language.unsafeNulls - import scala.annotation.tailrec - -import scala.collection.{ mutable, immutable } - +import scala.collection.{immutable, mutable} import scala.tools.asm import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.TreeTypeMap import dotty.tools.dotc.CompilationUnit -import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.NameKinds._ +import dotty.tools.dotc.ast.Trees.SyntheticUnit +import dotty.tools.dotc.core.Decorators.* +import dotty.tools.dotc.core.Flags.* +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.NameKinds.* import dotty.tools.dotc.core.Names.TermName -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.util.Spans._ +import dotty.tools.dotc.core.Symbols.* +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.util.Spans.* import dotty.tools.dotc.report -import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.transform.SymUtils.* /* * @@ -624,16 +622,17 @@ trait BCodeSkelBuilder extends BCodeHelpers { case _ => a } - if (!emitLines || !tree.span.exists) return; - val nr = ctx.source.offsetToLine(tree.span.point) + 1 - if (nr != lastEmittedLineNr) { - lastEmittedLineNr = nr - getNonLabelNode(lastInsn) match { - case lnn: asm.tree.LineNumberNode => - // overwrite previous landmark as no instructions have been emitted for it - lnn.line = nr - case _ => - mnode.visitLineNumber(nr, currProgramPoint()) + if (emitLines && tree.span.exists && !tree.hasAttachment(SyntheticUnit)) { + val nr = ctx.source.offsetToLine(tree.span.point) + 1 + if (nr != lastEmittedLineNr) { + lastEmittedLineNr = nr + getNonLabelNode(lastInsn) match { + case lnn: asm.tree.LineNumberNode => + // overwrite previous landmark as no instructions have been emitted for it + lnn.line = nr + case _ => + mnode.visitLineNumber(nr, currProgramPoint()) + } } } } diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 3f58e78ff7b1..e3fe2b9a2503 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1809,7 +1809,7 @@ object desugar { case ts: Thicket => ts.trees.tail case t => Nil } map { - case Block(Nil, EmptyTree) => Literal(Constant(())) // for s"... ${} ..." + case Block(Nil, EmptyTree) => unitLiteral // for s"... ${} ..." case Block(Nil, expr) => expr // important for interpolated string as patterns, see i1773.scala case t => t } @@ -1837,7 +1837,7 @@ object desugar { val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt)) flatTree(pats1 map (makePatDef(tree, mods, _, rhs))) case ext: ExtMethods => - Block(List(ext), Literal(Constant(())).withSpan(ext.span)) + Block(List(ext), unitLiteral.withSpan(ext.span)) case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt) } desugared.withSpan(tree.span) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 4884888a8959..79bef5cc1b0b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -31,6 +31,8 @@ object Trees { /** Property key for backquoted identifiers and definitions */ val Backquoted: Property.StickyKey[Unit] = Property.StickyKey() + + val SyntheticUnit: Property.StickyKey[Unit] = Property.StickyKey() /** Trees take a parameter indicating what the type of their `tpe` field * is. Two choices: `Type` or `Untyped`. diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 79e47ca7b8df..23ce290665da 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -64,7 +64,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.Literal(const)) def unitLiteral(using Context): Literal = - Literal(Constant(())) + Literal(Constant(())).withAttachment(SyntheticUnit, ()) def nullLiteral(using Context): Literal = Literal(Constant(null)) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e3488034fef8..3c51aa7f6a21 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -479,7 +479,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def InferredTypeTree(tpe: Type)(using Context): TypedSplice = TypedSplice(new InferredTypeTree().withTypeUnchecked(tpe)) - def unitLiteral(implicit src: SourceFile): Literal = Literal(Constant(())) + def unitLiteral(implicit src: SourceFile): Literal = Literal(Constant(())).withAttachment(SyntheticUnit, ()) def ref(tp: NamedType)(using Context): Tree = TypedSplice(tpd.ref(tp)) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 66a83c66b5f8..cabccb6e49f2 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -793,7 +793,7 @@ class Inliner(val call: tpd.Tree)(using Context): typed(tree.cond, defn.BooleanType)(using condCtx) match { case cond1 @ ConstantValue(b: Boolean) => val selected0 = if (b) tree.thenp else tree.elsep - val selected = if (selected0.isEmpty) tpd.Literal(Constant(())) else typed(selected0, pt) + val selected = if (selected0.isEmpty) tpd.unitLiteral else typed(selected0, pt) if (isIdempotentExpr(cond1)) selected else Block(cond1 :: Nil, selected) case cond1 => diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index eca2c666758e..98e7aeace3e2 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -408,7 +408,7 @@ object Inlines: arg match case ConstantValue(_) | Inlined(_, Nil, Typed(ConstantValue(_), _)) => // ok case _ => report.error(em"expected a constant value but found: $arg", arg.srcPos) - return Literal(Constant(())).withSpan(call.span) + return unitLiteral.withSpan(call.span) else if inlinedMethod == defn.Compiletime_codeOf then return Intrinsics.codeOf(arg, call.srcPos) case _ => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 210bc5815f69..5cd53cc2b48a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2180,7 +2180,7 @@ object Parsers { case _ => } patch(source, cond.span.endPos, "}) ()") - WhileDo(Block(body, cond), Literal(Constant(()))) + WhileDo(Block(body, cond), unitLiteral) } case TRY => val tryOffset = in.offset @@ -2210,7 +2210,7 @@ object Parsers { in.nextToken(); val expr = subExpr() if expr.span.exists then expr - else Literal(Constant(())) // finally without an expression + else unitLiteral // finally without an expression } else { if handler.isEmpty then @@ -3701,10 +3701,10 @@ object Parsers { val stats = selfInvocation() :: ( if (isStatSep) { in.nextToken(); blockStatSeq() } else Nil) - Block(stats, Literal(Constant(()))) + Block(stats, unitLiteral) } } - else Block(selfInvocation() :: Nil, Literal(Constant(()))) + else Block(selfInvocation() :: Nil, unitLiteral) /** SelfInvocation ::= this ArgumentExprs {ArgumentExprs} */ diff --git a/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala b/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala index 3081bd5c2b20..9f94a8c13a52 100644 --- a/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala +++ b/compiler/src/dotty/tools/dotc/transform/DropBreaks.scala @@ -122,7 +122,7 @@ class DropBreaks extends MiniPhase: case id: Ident => val arg = (args: @unchecked) match case arg :: Nil => arg - case Nil => Literal(Constant(())).withSpan(tree.span) + case Nil => unitLiteral.withSpan(tree.span) Some((id.symbol, arg)) case _ => None case _ => None diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index fce29afdd638..2ebf5f2ed954 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -317,7 +317,7 @@ object Erasure { cast(tree1, pt) case _ => val cls = pt.classSymbol - if (cls eq defn.UnitClass) constant(tree, Literal(Constant(()))) + if (cls eq defn.UnitClass) constant(tree, unitLiteral) else { assert(cls ne defn.ArrayClass) ref(unboxMethod(cls.asClass)).appliedTo(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 03ac15b39ffe..af6533cfc17f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -173,7 +173,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => myState.classesThatNeedReleaseFence += sym.owner val initializer = if isErasableBottomField(field, tree.termParamss.head.head.tpt.tpe.classSymbol) - then Literal(Constant(())) + then unitLiteral else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol))) val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym))) sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index ac1e1868f26e..a648a419d594 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -947,7 +947,7 @@ object PatternMatcher { case LabeledPlan(label, expr) => Labeled(label, emit(expr)) case ReturnPlan(label) => - Return(Literal(Constant(())), ref(label)) + Return(unitLiteral, ref(label)) case plan: SeqPlan => def default = seq(emit(plan.head) :: Nil, emit(plan.tail)) def maybeEmitSwitch(scrutinee: Tree): Tree = { diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 2c552eec6d12..556204ab89ab 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -285,7 +285,7 @@ object TypeTestsCasts { Typed(expr, tree.args.head) // Replace cast by type ascription (which does not generate any bytecode) else if (testCls eq defn.BoxedUnitClass) // as a special case, casting to Unit always successfully returns Unit - Block(expr :: Nil, Literal(Constant(()))).withSpan(expr.span) + Block(expr :: Nil, unitLiteral).withSpan(expr.span) else if (foundClsSymPrimitive) if (testCls.isPrimitiveValueClass) primitiveConversion(expr, testCls) else derivedTree(box(expr), defn.Any_asInstanceOf, testType) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 9ee7a672ffba..240cb3349df3 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1547,7 +1547,7 @@ class RefChecks extends MiniPhase { thisPhase => private def transformIf(tree: If): Tree = { val If(cond, thenpart, elsepart) = tree def unitIfEmpty(t: Tree): Tree = - if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t + if (t == EmptyTree) unitLiteral.setPos(tree.pos).setType(UnitTpe) else t cond.tpe match { case ConstantType(value) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 982895294cf0..a6ab751e65b5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4117,7 +4117,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (!ctx.isAfterTyper && !tree.isInstanceOf[Inlined] && ctx.settings.WvalueDiscard.value && !isThisTypeResult(tree)) { report.warning(ValueDiscarding(tree.tpe), tree.srcPos) } - return tpd.Block(tree1 :: Nil, Literal(Constant(()))) + return tpd.Block(tree1 :: Nil, unitLiteral) } // convert function literal to SAM closure diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index 70d3c72500b2..29119c8d081f 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -1682,6 +1682,25 @@ class DottyBytecodeTests extends DottyBytecodeTest { assertSameCode(instructions, expected) } } + + @Test def i18320(): Unit = { + val c1 = + """class C { + | def m: Unit = { + | val x = 1 + | } + |} + |""".stripMargin + checkBCode(c1) {dir => + val clsIn = dir.lookupName("C.class", directory = false).input + val clsNode = loadClassNode(clsIn, skipDebugInfo = false) + val method = getMethod(clsNode, "m") + val instructions = instructionsFromMethod(method).filter(_.isInstanceOf[LineNumber]) + val expected = List(LineNumber(3, Label(0))) + assertSameCode(instructions, expected) + + } + } } object invocationReceiversTestCode { From a56099cb64396381f51f8a8362112f52b754524a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 23 Jan 2023 11:02:15 +0100 Subject: [PATCH 106/134] Add changelog for 3.3.0-RC1 [Cherry-picked 5cc4a9cb1efea46c33322e8f01fffaf02245de69] [Cherry-picked acd5d3fca09997c1914a59df9e784cf18a3358fd] --- changelogs/3.3.0-RC1.md | 225 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 changelogs/3.3.0-RC1.md diff --git a/changelogs/3.3.0-RC1.md b/changelogs/3.3.0-RC1.md new file mode 100644 index 000000000000..1d632e49032a --- /dev/null +++ b/changelogs/3.3.0-RC1.md @@ -0,0 +1,225 @@ +# Highlights of the release + +- Stabilize new lazy vals [#16614](https://github.com/lampepfl/dotty/pull/16614) +- Experimental Macro annotations [#16392](https://github.com/lampepfl/dotty/pull/16392) [#16454](https://github.com/lampepfl/dotty/pull/16454) [#16534](https://github.com/lampepfl/dotty/pull/16534) +- Fix stability check for inline parameters [#15511](https://github.com/lampepfl/dotty/pull/15511) +- Make `fewerBraces` a standard feature [#16297](https://github.com/lampepfl/dotty/pull/16297) +- Add new front-end phase for unused entities and add support for unused imports [#16157](https://github.com/lampepfl/dotty/pull/16157) +- Implement -Wvalue-discard warning [#15975](https://github.com/lampepfl/dotty/pull/15975) +- Introduce boundary/break control abstraction. [#16612](https://github.com/lampepfl/dotty/pull/16612) + +# Other changes and fixes + +## Annotations + +- Support use-site meta-annotations [#16445](https://github.com/lampepfl/dotty/pull/16445) + +## Desugaring + +- Reuse typed prefix for `applyDynamic` and `applyDynamicNamed` [#16552](https://github.com/lampepfl/dotty/pull/16552) +- Fix object selftype match error [#16441](https://github.com/lampepfl/dotty/pull/16441) + +## Erasure + +- Dealias before checking for outer references in types [#16525](https://github.com/lampepfl/dotty/pull/16525) +- Fix generic signature for type params bounded by primitive [#16442](https://github.com/lampepfl/dotty/pull/16442) +- Avoid EmptyScope.cloneScope crashing, eg on missing references [#16314](https://github.com/lampepfl/dotty/pull/16314) + +## GADTs + +- Inline GADT state restoring in TypeComparer [#16564](https://github.com/lampepfl/dotty/pull/16564) +- Add extension/conversion to GADT selection healing [#16638](https://github.com/lampepfl/dotty/pull/16638) + +## Incremental compilation + +- Unpickle arguments of parent constructors in Templates lazily [#16688](https://github.com/lampepfl/dotty/pull/16688) + +## Initialization + +- Fix #16438: Supply dummy args for erroneous parent call in init check [#16448](https://github.com/lampepfl/dotty/pull/16448) + +## Inline + +- Dealias in ConstantValue, for inline if cond [#16652](https://github.com/lampepfl/dotty/pull/16652) +- Set Span for top level annotations generated in PostTyper [#16378](https://github.com/lampepfl/dotty/pull/16378) +- Interpolate any type vars from comparing against SelectionProto [#16348](https://github.com/lampepfl/dotty/pull/16348) +- Handle binding of beta reduced inlined lambdas [#16377](https://github.com/lampepfl/dotty/pull/16377) +- Do not add dummy RHS to abstract inline methods [#16510](https://github.com/lampepfl/dotty/pull/16510) +- Warn on inline given aliases with functions as RHS [#16499](https://github.com/lampepfl/dotty/pull/16499) +- Support inline overrides in value classes [#16523](https://github.com/lampepfl/dotty/pull/16523) + +## Java interop + +- Represent Java annotations as interfaces so they can be extended, and disallow various misuses of them [#16260](https://github.com/lampepfl/dotty/pull/16260) + +## Opaque Types + +- Delay opaque alias checking until PostTyper [#16644](https://github.com/lampepfl/dotty/pull/16644) + +## Overloading + +- Handle context function arguments in overloading resolution [#16511](https://github.com/lampepfl/dotty/pull/16511) + +## Parser + +- Improve support for Unicode supplementary characters in identifiers and string interpolation (as in Scala 2) [#16278](https://github.com/lampepfl/dotty/pull/16278) +- Require indent after colon at EOL [#16466](https://github.com/lampepfl/dotty/pull/16466) +- Help givens return refined types [#16293](https://github.com/lampepfl/dotty/pull/16293) + +## Pattern Matching + +- Tweak AvoidMap's derivedSelect [#16563](https://github.com/lampepfl/dotty/pull/16563) +- Space: Use RHS of & when refining subtypes [#16573](https://github.com/lampepfl/dotty/pull/16573) +- Freeze constraints in a condition check of maximiseType [#16526](https://github.com/lampepfl/dotty/pull/16526) +- Restrict syntax of typed patterns [#16150](https://github.com/lampepfl/dotty/pull/16150) +- Test case to show that #16252 works with transparent [#16262](https://github.com/lampepfl/dotty/pull/16262) +- Support inline unapplySeq and with leading given parameters [#16358](https://github.com/lampepfl/dotty/pull/16358) +- Handle sealed prefixes in exh checking [#16621](https://github.com/lampepfl/dotty/pull/16621) +- Detect irrefutable quoted patterns [#16674](https://github.com/lampepfl/dotty/pull/16674) + +## Pickling + +- Allow case classes with up to 254 parameters [#16501](https://github.com/lampepfl/dotty/pull/16501) +- Correctly unpickle Scala 2 private case classes in traits [#16519](https://github.com/lampepfl/dotty/pull/16519) + +## Polyfunctions + +- Fix #9996: Crash with function accepting polymorphic function type with singleton result [#16327](https://github.com/lampepfl/dotty/pull/16327) + +## Quotes + +- Remove contents of inline methods [#16345](https://github.com/lampepfl/dotty/pull/16345) +- Fix errors in explicit type annotations in inline match cases [#16257](https://github.com/lampepfl/dotty/pull/16257) +- Handle macro annotation suspends and crashes [#16509](https://github.com/lampepfl/dotty/pull/16509) +- Fix macro annotations `spliceOwner` [#16513](https://github.com/lampepfl/dotty/pull/16513) + +## REPL + +- REPL: Fix crash when printing instances of value classes [#16393](https://github.com/lampepfl/dotty/pull/16393) +- Attempt to fix completion crash [#16267](https://github.com/lampepfl/dotty/pull/16267) +- Fix REPL shadowing bug [#16389](https://github.com/lampepfl/dotty/pull/16389) +- Open up for extensibility [#16276](https://github.com/lampepfl/dotty/pull/16276) +- Don't crash if completions throw [#16687](https://github.com/lampepfl/dotty/pull/16687) + +## Reflection + +- Fix reflect typeMembers to return all members [#15033](https://github.com/lampepfl/dotty/pull/15033) +- Deprecate reflect Flags.Static [#16568](https://github.com/lampepfl/dotty/pull/16568) + +## Reporting + +- Suppress follow-on errors for erroneous import qualifiers [#16658](https://github.com/lampepfl/dotty/pull/16658) +- Fix order in which errors are reported for assignment to val [#16660](https://github.com/lampepfl/dotty/pull/16660) +- Fix class name in error message [#16635](https://github.com/lampepfl/dotty/pull/16635) +- Make refined type printing more source compatible [#16303](https://github.com/lampepfl/dotty/pull/16303) +- Add error hint on local inline def used in quotes [#16572](https://github.com/lampepfl/dotty/pull/16572) +- Fix Text wrapping [#16277](https://github.com/lampepfl/dotty/pull/16277) +- Fix -Wunused:import registering constructor `` instead of its owner (also fix false positive for enum) [#16661](https://github.com/lampepfl/dotty/pull/16661) +- Fix #16675 : -Wunused false positive on case class generated method, due to flags used to distinguish case accessors. [#16683](https://github.com/lampepfl/dotty/pull/16683) +- Fix #16680 by registering Ident not containing a symbol [#16689](https://github.com/lampepfl/dotty/pull/16689) +- Fix #16682: CheckUnused missed some used symbols [#16690](https://github.com/lampepfl/dotty/pull/16690) +- Fix the non-miniphase tree traverser [#16684](https://github.com/lampepfl/dotty/pull/16684) + +## Scala-JS + +- Fix #14289: Accept Ident refs to `js.native` in native member rhs. [#16185](https://github.com/lampepfl/dotty/pull/16185) + +## Standard Library + +- Add `CanEqual` instance for `Map` [#15886](https://github.com/lampepfl/dotty/pull/15886) +- Refine `Tuple.Append` return type [#16140](https://github.com/lampepfl/dotty/pull/16140) + +## TASTy format + +- Make it a fatal error if erasure cannot resolve a type [#16373](https://github.com/lampepfl/dotty/pull/16373) + +## Tooling + +- Add -Yimports compiler flag [#16218](https://github.com/lampepfl/dotty/pull/16218) +- Allow BooleanSettings to be set with a colon [#16425](https://github.com/lampepfl/dotty/pull/16425) + +## Transform + +- Avoid stackoverflow in ExplicitOuter [#16381](https://github.com/lampepfl/dotty/pull/16381) +- Make lazy vals run on non-fallback graal image - remove dynamic reflection [#16346](https://github.com/lampepfl/dotty/pull/16346) +- Patch to avoid crash in #16351 [#16354](https://github.com/lampepfl/dotty/pull/16354) +- Don't treat package object's `` methods as package members [#16667](https://github.com/lampepfl/dotty/pull/16667) +- Space: Refine isSubspace property & an example [#16574](https://github.com/lampepfl/dotty/pull/16574) + +## Typer + +- Drop requirement that self types are closed [#16648](https://github.com/lampepfl/dotty/pull/16648) +- Disallow constructor params from appearing in parent types for soundness [#16664](https://github.com/lampepfl/dotty/pull/16664) +- Don't search implicit arguments in singleton type prefix [#16490](https://github.com/lampepfl/dotty/pull/16490) +- Don't rely on isProvisional to determine whether atoms computed [#16489](https://github.com/lampepfl/dotty/pull/16489) +- Support signature polymorphic methods (`MethodHandle` and `VarHandle`) [#16225](https://github.com/lampepfl/dotty/pull/16225) +- Prefer parameterless alternatives during ambiguous overload resolution [#16315](https://github.com/lampepfl/dotty/pull/16315) +- Fix calculation to drop transparent classes [#16344](https://github.com/lampepfl/dotty/pull/16344) +- Test case for issue 16311 [#16317](https://github.com/lampepfl/dotty/pull/16317) +- Skip caching provisional OrType atoms [#16295](https://github.com/lampepfl/dotty/pull/16295) +- Avoid cyclic references due to experimental check when inlining [#16195](https://github.com/lampepfl/dotty/pull/16195) +- Track type variable dependencies to guide instantiation decisions [#16042](https://github.com/lampepfl/dotty/pull/16042) +- Two fixes to constraint solving [#16353](https://github.com/lampepfl/dotty/pull/16353) +- Fix regression in cyclic constraint handling [#16514](https://github.com/lampepfl/dotty/pull/16514) +- Sharpen range approximation for applied types with capture set ranges [#16261](https://github.com/lampepfl/dotty/pull/16261) +- Cut the Gordian Knot: Don't widen unions to transparent [#15642](https://github.com/lampepfl/dotty/pull/15642) +- Fix widening logic to keep instantiation within bounds [#16417](https://github.com/lampepfl/dotty/pull/16417) +- Skip ambiguous reference error when symbols are aliases [#16401](https://github.com/lampepfl/dotty/pull/16401) +- Avoid incorrect simplifications when updating bounds in the constraint [#16410](https://github.com/lampepfl/dotty/pull/16410) +- Take `@targetName` into account when resolving extension methods [#16487](https://github.com/lampepfl/dotty/pull/16487) +- Improve ClassTag handling to avoid invalid ClassTag generation and inference failure [#16492](https://github.com/lampepfl/dotty/pull/16492) +- Fix extracting the elemType of a union of arrays [#16569](https://github.com/lampepfl/dotty/pull/16569) +- Make sure annotations are typed in expression contexts [#16699](https://github.com/lampepfl/dotty/pull/16699) +- Throw a type error when using hk-types in unions or intersections [#16712](https://github.com/lampepfl/dotty/pull/16712) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.2.2..3.3.0-RC1` these are: + +``` + 225 Martin Odersky + 73 Dale Wijnand + 58 Szymon Rodziewicz + 54 Nicolas Stucki + 48 Kamil Szewczyk + 48 Paul Coral + 30 Paweł Marks + 28 Florian3k + 28 Yichen Xu + 14 Guillaume Martres + 8 Fengyun Liu + 8 Michał Pałka + 7 Chris Birchall + 7 rochala + 6 Kacper Korban + 6 Sébastien Doeraene + 6 jdudrak + 5 Seth Tisue + 5 Som Snytt + 5 nizhikov + 4 Filip Zybała + 4 Jan Chyb + 4 Michael Pollmeier + 4 Natsu Kagami + 3 Jamie Thompson + 2 Alex + 2 Anatolii Kmetiuk + 2 Dmitrii Naumenko + 2 Lukas Rytz + 2 adampauls + 2 yoshinorin + 1 Alexander Slesarenko + 1 Chris Kipp + 1 Guillaume Raffin + 1 Jakub Kozłowski + 1 Jan-Pieter van den Heuvel + 1 Julien Richard-Foy + 1 Kenji Yoshida + 1 Philippus + 1 Szymon R + 1 Tim Spence + 1 s.bazarsadaev + +``` \ No newline at end of file From 73456eccbe418186ad7ec4a657323f1880804eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 26 Jan 2023 15:24:51 +0100 Subject: [PATCH 107/134] Add changelog for 3.3.0-RC2 [Cherry-picked 57a6de25f532c8ac6d7ed2ee1ee067e0599d524e] [Cherry-picked 720c139a79e4998284a531733e4b549871b9c243] --- changelogs/3.3.0-RC2.md | 229 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 changelogs/3.3.0-RC2.md diff --git a/changelogs/3.3.0-RC2.md b/changelogs/3.3.0-RC2.md new file mode 100644 index 000000000000..57d785816489 --- /dev/null +++ b/changelogs/3.3.0-RC2.md @@ -0,0 +1,229 @@ +This release is nearly identical to 3.3.0-RC1. The only difference is that 3.3.0-RC1 generated output with incorrect TASTy version. + +The following changelog is identical to the changelog of 3.3.0-RC1. + +# Highlights of the release + +- Stabilize new lazy vals [#16614](https://github.com/lampepfl/dotty/pull/16614) +- Experimental Macro annotations [#16392](https://github.com/lampepfl/dotty/pull/16392) [#16454](https://github.com/lampepfl/dotty/pull/16454) [#16534](https://github.com/lampepfl/dotty/pull/16534) +- Fix stability check for inline parameters [#15511](https://github.com/lampepfl/dotty/pull/15511) +- Make `fewerBraces` a standard feature [#16297](https://github.com/lampepfl/dotty/pull/16297) +- Add new front-end phase for unused entities and add support for unused imports [#16157](https://github.com/lampepfl/dotty/pull/16157) +- Implement -Wvalue-discard warning [#15975](https://github.com/lampepfl/dotty/pull/15975) +- Introduce boundary/break control abstraction. [#16612](https://github.com/lampepfl/dotty/pull/16612) + +# Other changes and fixes + +## Annotations + +- Support use-site meta-annotations [#16445](https://github.com/lampepfl/dotty/pull/16445) + +## Desugaring + +- Reuse typed prefix for `applyDynamic` and `applyDynamicNamed` [#16552](https://github.com/lampepfl/dotty/pull/16552) +- Fix object selftype match error [#16441](https://github.com/lampepfl/dotty/pull/16441) + +## Erasure + +- Dealias before checking for outer references in types [#16525](https://github.com/lampepfl/dotty/pull/16525) +- Fix generic signature for type params bounded by primitive [#16442](https://github.com/lampepfl/dotty/pull/16442) +- Avoid EmptyScope.cloneScope crashing, eg on missing references [#16314](https://github.com/lampepfl/dotty/pull/16314) + +## GADTs + +- Inline GADT state restoring in TypeComparer [#16564](https://github.com/lampepfl/dotty/pull/16564) +- Add extension/conversion to GADT selection healing [#16638](https://github.com/lampepfl/dotty/pull/16638) + +## Incremental compilation + +- Unpickle arguments of parent constructors in Templates lazily [#16688](https://github.com/lampepfl/dotty/pull/16688) + +## Initialization + +- Fix #16438: Supply dummy args for erroneous parent call in init check [#16448](https://github.com/lampepfl/dotty/pull/16448) + +## Inline + +- Dealias in ConstantValue, for inline if cond [#16652](https://github.com/lampepfl/dotty/pull/16652) +- Set Span for top level annotations generated in PostTyper [#16378](https://github.com/lampepfl/dotty/pull/16378) +- Interpolate any type vars from comparing against SelectionProto [#16348](https://github.com/lampepfl/dotty/pull/16348) +- Handle binding of beta reduced inlined lambdas [#16377](https://github.com/lampepfl/dotty/pull/16377) +- Do not add dummy RHS to abstract inline methods [#16510](https://github.com/lampepfl/dotty/pull/16510) +- Warn on inline given aliases with functions as RHS [#16499](https://github.com/lampepfl/dotty/pull/16499) +- Support inline overrides in value classes [#16523](https://github.com/lampepfl/dotty/pull/16523) + +## Java interop + +- Represent Java annotations as interfaces so they can be extended, and disallow various misuses of them [#16260](https://github.com/lampepfl/dotty/pull/16260) + +## Opaque Types + +- Delay opaque alias checking until PostTyper [#16644](https://github.com/lampepfl/dotty/pull/16644) + +## Overloading + +- Handle context function arguments in overloading resolution [#16511](https://github.com/lampepfl/dotty/pull/16511) + +## Parser + +- Improve support for Unicode supplementary characters in identifiers and string interpolation (as in Scala 2) [#16278](https://github.com/lampepfl/dotty/pull/16278) +- Require indent after colon at EOL [#16466](https://github.com/lampepfl/dotty/pull/16466) +- Help givens return refined types [#16293](https://github.com/lampepfl/dotty/pull/16293) + +## Pattern Matching + +- Tweak AvoidMap's derivedSelect [#16563](https://github.com/lampepfl/dotty/pull/16563) +- Space: Use RHS of & when refining subtypes [#16573](https://github.com/lampepfl/dotty/pull/16573) +- Freeze constraints in a condition check of maximiseType [#16526](https://github.com/lampepfl/dotty/pull/16526) +- Restrict syntax of typed patterns [#16150](https://github.com/lampepfl/dotty/pull/16150) +- Test case to show that #16252 works with transparent [#16262](https://github.com/lampepfl/dotty/pull/16262) +- Support inline unapplySeq and with leading given parameters [#16358](https://github.com/lampepfl/dotty/pull/16358) +- Handle sealed prefixes in exh checking [#16621](https://github.com/lampepfl/dotty/pull/16621) +- Detect irrefutable quoted patterns [#16674](https://github.com/lampepfl/dotty/pull/16674) + +## Pickling + +- Allow case classes with up to 254 parameters [#16501](https://github.com/lampepfl/dotty/pull/16501) +- Correctly unpickle Scala 2 private case classes in traits [#16519](https://github.com/lampepfl/dotty/pull/16519) + +## Polyfunctions + +- Fix #9996: Crash with function accepting polymorphic function type with singleton result [#16327](https://github.com/lampepfl/dotty/pull/16327) + +## Quotes + +- Remove contents of inline methods [#16345](https://github.com/lampepfl/dotty/pull/16345) +- Fix errors in explicit type annotations in inline match cases [#16257](https://github.com/lampepfl/dotty/pull/16257) +- Handle macro annotation suspends and crashes [#16509](https://github.com/lampepfl/dotty/pull/16509) +- Fix macro annotations `spliceOwner` [#16513](https://github.com/lampepfl/dotty/pull/16513) + +## REPL + +- REPL: Fix crash when printing instances of value classes [#16393](https://github.com/lampepfl/dotty/pull/16393) +- Attempt to fix completion crash [#16267](https://github.com/lampepfl/dotty/pull/16267) +- Fix REPL shadowing bug [#16389](https://github.com/lampepfl/dotty/pull/16389) +- Open up for extensibility [#16276](https://github.com/lampepfl/dotty/pull/16276) +- Don't crash if completions throw [#16687](https://github.com/lampepfl/dotty/pull/16687) + +## Reflection + +- Fix reflect typeMembers to return all members [#15033](https://github.com/lampepfl/dotty/pull/15033) +- Deprecate reflect Flags.Static [#16568](https://github.com/lampepfl/dotty/pull/16568) + +## Reporting + +- Suppress follow-on errors for erroneous import qualifiers [#16658](https://github.com/lampepfl/dotty/pull/16658) +- Fix order in which errors are reported for assignment to val [#16660](https://github.com/lampepfl/dotty/pull/16660) +- Fix class name in error message [#16635](https://github.com/lampepfl/dotty/pull/16635) +- Make refined type printing more source compatible [#16303](https://github.com/lampepfl/dotty/pull/16303) +- Add error hint on local inline def used in quotes [#16572](https://github.com/lampepfl/dotty/pull/16572) +- Fix Text wrapping [#16277](https://github.com/lampepfl/dotty/pull/16277) +- Fix -Wunused:import registering constructor `` instead of its owner (also fix false positive for enum) [#16661](https://github.com/lampepfl/dotty/pull/16661) +- Fix #16675 : -Wunused false positive on case class generated method, due to flags used to distinguish case accessors. [#16683](https://github.com/lampepfl/dotty/pull/16683) +- Fix #16680 by registering Ident not containing a symbol [#16689](https://github.com/lampepfl/dotty/pull/16689) +- Fix #16682: CheckUnused missed some used symbols [#16690](https://github.com/lampepfl/dotty/pull/16690) +- Fix the non-miniphase tree traverser [#16684](https://github.com/lampepfl/dotty/pull/16684) + +## Scala-JS + +- Fix #14289: Accept Ident refs to `js.native` in native member rhs. [#16185](https://github.com/lampepfl/dotty/pull/16185) + +## Standard Library + +- Add `CanEqual` instance for `Map` [#15886](https://github.com/lampepfl/dotty/pull/15886) +- Refine `Tuple.Append` return type [#16140](https://github.com/lampepfl/dotty/pull/16140) + +## TASTy format + +- Make it a fatal error if erasure cannot resolve a type [#16373](https://github.com/lampepfl/dotty/pull/16373) + +## Tooling + +- Add -Yimports compiler flag [#16218](https://github.com/lampepfl/dotty/pull/16218) +- Allow BooleanSettings to be set with a colon [#16425](https://github.com/lampepfl/dotty/pull/16425) + +## Transform + +- Avoid stackoverflow in ExplicitOuter [#16381](https://github.com/lampepfl/dotty/pull/16381) +- Make lazy vals run on non-fallback graal image - remove dynamic reflection [#16346](https://github.com/lampepfl/dotty/pull/16346) +- Patch to avoid crash in #16351 [#16354](https://github.com/lampepfl/dotty/pull/16354) +- Don't treat package object's `` methods as package members [#16667](https://github.com/lampepfl/dotty/pull/16667) +- Space: Refine isSubspace property & an example [#16574](https://github.com/lampepfl/dotty/pull/16574) + +## Typer + +- Drop requirement that self types are closed [#16648](https://github.com/lampepfl/dotty/pull/16648) +- Disallow constructor params from appearing in parent types for soundness [#16664](https://github.com/lampepfl/dotty/pull/16664) +- Don't search implicit arguments in singleton type prefix [#16490](https://github.com/lampepfl/dotty/pull/16490) +- Don't rely on isProvisional to determine whether atoms computed [#16489](https://github.com/lampepfl/dotty/pull/16489) +- Support signature polymorphic methods (`MethodHandle` and `VarHandle`) [#16225](https://github.com/lampepfl/dotty/pull/16225) +- Prefer parameterless alternatives during ambiguous overload resolution [#16315](https://github.com/lampepfl/dotty/pull/16315) +- Fix calculation to drop transparent classes [#16344](https://github.com/lampepfl/dotty/pull/16344) +- Test case for issue 16311 [#16317](https://github.com/lampepfl/dotty/pull/16317) +- Skip caching provisional OrType atoms [#16295](https://github.com/lampepfl/dotty/pull/16295) +- Avoid cyclic references due to experimental check when inlining [#16195](https://github.com/lampepfl/dotty/pull/16195) +- Track type variable dependencies to guide instantiation decisions [#16042](https://github.com/lampepfl/dotty/pull/16042) +- Two fixes to constraint solving [#16353](https://github.com/lampepfl/dotty/pull/16353) +- Fix regression in cyclic constraint handling [#16514](https://github.com/lampepfl/dotty/pull/16514) +- Sharpen range approximation for applied types with capture set ranges [#16261](https://github.com/lampepfl/dotty/pull/16261) +- Cut the Gordian Knot: Don't widen unions to transparent [#15642](https://github.com/lampepfl/dotty/pull/15642) +- Fix widening logic to keep instantiation within bounds [#16417](https://github.com/lampepfl/dotty/pull/16417) +- Skip ambiguous reference error when symbols are aliases [#16401](https://github.com/lampepfl/dotty/pull/16401) +- Avoid incorrect simplifications when updating bounds in the constraint [#16410](https://github.com/lampepfl/dotty/pull/16410) +- Take `@targetName` into account when resolving extension methods [#16487](https://github.com/lampepfl/dotty/pull/16487) +- Improve ClassTag handling to avoid invalid ClassTag generation and inference failure [#16492](https://github.com/lampepfl/dotty/pull/16492) +- Fix extracting the elemType of a union of arrays [#16569](https://github.com/lampepfl/dotty/pull/16569) +- Make sure annotations are typed in expression contexts [#16699](https://github.com/lampepfl/dotty/pull/16699) +- Throw a type error when using hk-types in unions or intersections [#16712](https://github.com/lampepfl/dotty/pull/16712) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.2.2..3.3.0-RC1` these are: + +``` + 225 Martin Odersky + 73 Dale Wijnand + 58 Szymon Rodziewicz + 54 Nicolas Stucki + 48 Kamil Szewczyk + 48 Paul Coral + 30 Paweł Marks + 28 Florian3k + 28 Yichen Xu + 14 Guillaume Martres + 8 Fengyun Liu + 8 Michał Pałka + 7 Chris Birchall + 7 rochala + 6 Kacper Korban + 6 Sébastien Doeraene + 6 jdudrak + 5 Seth Tisue + 5 Som Snytt + 5 nizhikov + 4 Filip Zybała + 4 Jan Chyb + 4 Michael Pollmeier + 4 Natsu Kagami + 3 Jamie Thompson + 2 Alex + 2 Anatolii Kmetiuk + 2 Dmitrii Naumenko + 2 Lukas Rytz + 2 adampauls + 2 yoshinorin + 1 Alexander Slesarenko + 1 Chris Kipp + 1 Guillaume Raffin + 1 Jakub Kozłowski + 1 Jan-Pieter van den Heuvel + 1 Julien Richard-Foy + 1 Kenji Yoshida + 1 Philippus + 1 Szymon R + 1 Tim Spence + 1 s.bazarsadaev + +``` \ No newline at end of file From f59cf887cfbe1b27a5870373e9f4158ddceeea7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 17 Feb 2023 13:33:31 +0100 Subject: [PATCH 108/134] Add changelog for 3.3.0-RC3 [Cherry-picked 8562128549869d4e9aa2db5bf5c215a3a6b49f33] [Cherry-picked d8dd74da1abb35c93456c7b23a1e20aacf041c31] --- changelogs/3.3.0-RC3.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 changelogs/3.3.0-RC3.md diff --git a/changelogs/3.3.0-RC3.md b/changelogs/3.3.0-RC3.md new file mode 100644 index 000000000000..79a47fcf0bb9 --- /dev/null +++ b/changelogs/3.3.0-RC3.md @@ -0,0 +1,23 @@ +# Backported fixes + +- Added jpath check to `ClassLikeSupport` getParentsAsTreeSymbolTuples [#16759](https://github.com/lampepfl/dotty/pull/16759) +- Split out immutable GadtConstraint [#16602](https://github.com/lampepfl/dotty/pull/16602) +- Avoid bidirectional GADT typebounds from fullBounds [#15683](https://github.com/lampepfl/dotty/pull/15683) +- Fix static lazy field holder for GraalVM [#16800](https://github.com/lampepfl/dotty/pull/16800) +- Add support for disabling redirected output in the REPL driver for usage in worksheets in the Scala Plugin for IntelliJ IDEA [#16810](https://github.com/lampepfl/dotty/pull/16810) +- Add missing criterion to subtype check [#16889](https://github.com/lampepfl/dotty/pull/16889) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0-RC2..3.3.0-RC3` these are: + +``` + 7 Dale Wijnand + 5 Szymon Rodziewicz + 2 Paweł Marks + 2 Vasil Vasilev + 1 Martin Odersky + 1 Mohammad Yousuf Minhaj Zia +``` From 2888fdddf86f07c82d2313899904e0d752f3d16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 17 Apr 2023 12:55:54 +0200 Subject: [PATCH 109/134] Add changelog for 3.3.0-RC4 [Cherry-picked 22e6ffe7529bd888275218c02b377207e9fc2c0c] [Cherry-picked ccf2b28c4a8457d92332b5d67e877bc07e463167] --- changelogs/3.3.0-RC4.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 changelogs/3.3.0-RC4.md diff --git a/changelogs/3.3.0-RC4.md b/changelogs/3.3.0-RC4.md new file mode 100644 index 000000000000..4c4a490237b6 --- /dev/null +++ b/changelogs/3.3.0-RC4.md @@ -0,0 +1,35 @@ +# Backported fixes + +- Fix HK quoted pattern type variables [#16907](https//github.com/lampepfl/dotty/pull/16907) +- Fix caching issue caused by incorrect isProvisional check [#16989](https://github.com/lampepfl/dotty/pull/16989) +- Fix race condition in new LazyVals [#16975](https://github.com/lampepfl/dotty/pull/16975) +- Fix "-Wunused: False positive on parameterless enum member" [#16927](https://github.com/lampepfl/dotty/pull/16927) +- Register usage of symbols in non-inferred type trees in CheckUnused [#16939](https://github.com/lampepfl/dotty/pull/16939) +- Traverse annotations instead of just registering in -W [#16956](https://github.com/lampepfl/dotty/pull/16956) +- Ignore parameter of accessors in -Wunused [#16957](https://github.com/lampepfl/dotty/pull/16957) +- Improve override detection in CheckUnused [#16965](https://github.com/lampepfl/dotty/pull/16965) +- WUnused: Fix unused warning in synthetic symbols [#17020](https://github.com/lampepfl/dotty/pull/17020) +- Fix WUnused with idents in derived code [#17095](https//github.com/lampepfl/dotty/pull/17095) +- WUnused: Fix for symbols with synthetic names and unused transparent inlines [#17061](https//github.com/lampepfl/dotty/pull/17061) +- Skip extension method params in WUnused [#17178](https//github.com/lampepfl/dotty/pull/17178) +- Fix wunused false positive when deriving alias type [#17157](https//github.com/lampepfl/dotty/pull/17157) +- Fix WUnused for accessible symbols that are renamed [#17177](https//github.com/lampepfl/dotty/pull/17177) +- Fix WUnused false positive in for [#17176](https//github.com/lampepfl/dotty/pull/17176) +- Make CheckUnused run both after Typer and Inlining [#17206](https//github.com/lampepfl/dotty/pull/17206) +- Disable WUnused for params of non-private defs [#17223](https//github.com/lampepfl/dotty/pull/17223) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0-RC3..3.3.0-RC4` these are: + +``` + 41 Szymon Rodziewicz + 4 Paul Coral + 3 Paweł Marks + 1 Guillaume Martres + 1 Kacper Korban + 1 Nicolas Stucki + +``` From 491d839d9fa2d6672f2abdffdb4bafae406de22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 27 Apr 2023 10:16:47 +0200 Subject: [PATCH 110/134] Add changelog for 3.3.0-RC5 [Cherry-picked 909b56c0d4b40901053af1dad3858a882286c855] [Cherry-picked c0d85b99d1e51224d9744e7a1e6257b695e2d7dd] --- changelogs/3.3.0-RC5.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 changelogs/3.3.0-RC5.md diff --git a/changelogs/3.3.0-RC5.md b/changelogs/3.3.0-RC5.md new file mode 100644 index 000000000000..a9cc120ae39a --- /dev/null +++ b/changelogs/3.3.0-RC5.md @@ -0,0 +1,22 @@ +# Backported fixes + +- Remove experimental from `Mirror#fromProductTyped` [#16829](https//github.com/lampepfl/dotty/pull/16829) +- Wunused: Check if symbol exists before `isValidMemberDef` check [#17316](https://github.com/lampepfl/dotty/pull/17316) +- Wunused: Include import selector bounds in unused checks [#17323](https://github.com/lampepfl/dotty/pull/17323) +- Fix compiler crash in WUnused [#17340](https://github.com/lampepfl/dotty/pull/17340) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0-RC4..3.3.0-RC5` these are: + +``` + 2 Kacper Korban + 2 Michael Pilquist + 2 Paweł Marks + 2 Szymon Rodziewicz + 1 Matt Bovel + + +``` From f680b22917e1c4ea20ff78a899d95e381430937b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 12 May 2023 13:29:49 +0200 Subject: [PATCH 111/134] Add changelog for 3.3.0-RC6 [Cherry-picked 58256dd0fd1aa78ced4b1587275f7150d670d71e] [Cherry-picked 8de9a1b42e2592c0b7197425007d52e6b16a8498] --- changelogs/3.3.0-RC6.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 changelogs/3.3.0-RC6.md diff --git a/changelogs/3.3.0-RC6.md b/changelogs/3.3.0-RC6.md new file mode 100644 index 000000000000..ab98f0055974 --- /dev/null +++ b/changelogs/3.3.0-RC6.md @@ -0,0 +1,18 @@ +# Backported fixes + +- Patmat: Use less type variables in prefix inference [#16827](https//github.com/lampepfl/dotty/pull/16827) +- Just warn on type ascription on a pattern [#17454](https://github.com/lampepfl/dotty/pull/17454) +- Fix #17187: allow patches with same span [#17366](https://github.com/lampepfl/dotty/pull/17366) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0-RC5..3.3.0-RC6` these are: + +``` + 2 Adrien Piquerez + 2 Michał Pałka + 2 Paweł Marks + 1 Dale Wijnand +``` From 522c5c1bfbbbd49bb7b308d93247db1adab1e13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 May 2023 13:02:20 +0200 Subject: [PATCH 112/134] Add changelog for 3.3.0 [Cherry-picked 92152f4225890c93cb6c2660dadfff9519d1f1e8] [Cherry-picked c8a3e325a8aabb7df983dede41a25f1ced139399] --- changelogs/3.3.0.md | 268 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 changelogs/3.3.0.md diff --git a/changelogs/3.3.0.md b/changelogs/3.3.0.md new file mode 100644 index 000000000000..e3cc3703fadd --- /dev/null +++ b/changelogs/3.3.0.md @@ -0,0 +1,268 @@ +# Highlights of the release + +- Stabilize new lazy vals [#16614](https://github.com/lampepfl/dotty/pull/16614) +- Experimental Macro annotations [#16392](https://github.com/lampepfl/dotty/pull/16392) [#16454](https://github.com/lampepfl/dotty/pull/16454) [#16534](https://github.com/lampepfl/dotty/pull/16534) +- Fix stability check for inline parameters [#15511](https://github.com/lampepfl/dotty/pull/15511) +- Make `fewerBraces` a standard feature [#16297](https://github.com/lampepfl/dotty/pull/16297) +- Add new front-end phase for unused entities and add support for unused imports [#16157](https://github.com/lampepfl/dotty/pull/16157) +- Implement -Wvalue-discard warning [#15975](https://github.com/lampepfl/dotty/pull/15975) +- Introduce boundary/break control abstraction. [#16612](https://github.com/lampepfl/dotty/pull/16612) + +# Other changes and fixes + +## Annotations + +- Support use-site meta-annotations [#16445](https://github.com/lampepfl/dotty/pull/16445) + +## Desugaring + +- Reuse typed prefix for `applyDynamic` and `applyDynamicNamed` [#16552](https://github.com/lampepfl/dotty/pull/16552) +- Fix object selftype match error [#16441](https://github.com/lampepfl/dotty/pull/16441) + +## Erasure + +- Dealias before checking for outer references in types [#16525](https://github.com/lampepfl/dotty/pull/16525) +- Fix generic signature for type params bounded by primitive [#16442](https://github.com/lampepfl/dotty/pull/16442) +- Avoid EmptyScope.cloneScope crashing, eg on missing references [#16314](https://github.com/lampepfl/dotty/pull/16314) + +## GADTs + +- Inline GADT state restoring in TypeComparer [#16564](https://github.com/lampepfl/dotty/pull/16564) +- Add extension/conversion to GADT selection healing [#16638](https://github.com/lampepfl/dotty/pull/16638) +- Split out immutable GadtConstraint [#16602](https://github.com/lampepfl/dotty/pull/16602) +- Avoid bidirectional GADT typebounds from fullBounds [#15683](https://github.com/lampepfl/dotty/pull/15683) + +## Incremental compilation + +- Unpickle arguments of parent constructors in Templates lazily [#16688](https://github.com/lampepfl/dotty/pull/16688) + +## Initialization + +- Fix #16438: Supply dummy args for erroneous parent call in init check [#16448](https://github.com/lampepfl/dotty/pull/16448) + +## Inline + +- Dealias in ConstantValue, for inline if cond [#16652](https://github.com/lampepfl/dotty/pull/16652) +- Set Span for top level annotations generated in PostTyper [#16378](https://github.com/lampepfl/dotty/pull/16378) +- Interpolate any type vars from comparing against SelectionProto [#16348](https://github.com/lampepfl/dotty/pull/16348) +- Handle binding of beta reduced inlined lambdas [#16377](https://github.com/lampepfl/dotty/pull/16377) +- Do not add dummy RHS to abstract inline methods [#16510](https://github.com/lampepfl/dotty/pull/16510) +- Warn on inline given aliases with functions as RHS [#16499](https://github.com/lampepfl/dotty/pull/16499) +- Support inline overrides in value classes [#16523](https://github.com/lampepfl/dotty/pull/16523) + +## Java interop + +- Represent Java annotations as interfaces so they can be extended, and disallow various misuses of them [#16260](https://github.com/lampepfl/dotty/pull/16260) + +## Linting + +- Fix -Wunused:import registering constructor `` instead of its owner (also fix false positive for enum) [#16661](https://github.com/lampepfl/dotty/pull/16661) +- Fix #16675 : -Wunused false positive on case class generated method, due to flags used to distinguish case accessors. [#16683](https://github.com/lampepfl/dotty/pull/16683) +- Fix #16682: CheckUnused missed some used symbols [#16690](https://github.com/lampepfl/dotty/pull/16690) +- Fix "-Wunused: False positive on parameterless enum member" [#16927](https://github.com/lampepfl/dotty/pull/16927) +- Register usage of symbols in non-inferred type trees in CheckUnused [#16939](https://github.com/lampepfl/dotty/pull/16939) +- Traverse annotations instead of just registering in -Wunused [#16956](https://github.com/lampepfl/dotty/pull/16956) +- Ignore parameter of accessors in -Wunused [#16957](https://github.com/lampepfl/dotty/pull/16957) +- Ignore parameter of accessors in -Wunused [#16957](https://github.com/lampepfl/dotty/pull/16957) +- Improve override detection in CheckUnused [#16965](https://github.com/lampepfl/dotty/pull/16965) +- WUnused: Fix unused warning in synthetic symbols [#17020](https://github.com/lampepfl/dotty/pull/17020) +- Fix WUnused with idents in derived code [#17095](https//github.com/lampepfl/dotty/pull/17095) +- WUnused: Fix for symbols with synthetic names and unused transparent inlines [#17061](https//github.com/lampepfl/dotty/pull/17061) +- Skip extension method params in WUnused [#17178](https//github.com/lampepfl/dotty/pull/17178) +- Fix wunused false positive when deriving alias type [#17157](https//github.com/lampepfl/dotty/pull/17157) +- Fix WUnused for accessible symbols that are renamed [#17177](https//github.com/lampepfl/dotty/pull/17177) +- Fix WUnused false positive in for [#17176](https//github.com/lampepfl/dotty/pull/17176) +- Make CheckUnused run both after Typer and Inlining [#17206](https//github.com/lampepfl/dotty/pull/17206) +- Disable WUnused for params of non-private defs [#17223](https//github.com/lampepfl/dotty/pull/17223) +- Wunused: Check if symbol exists before `isValidMemberDef` check [#17316](https://github.com/lampepfl/dotty/pull/17316) +- Wunused: Include import selector bounds in unused checks [#17323](https://github.com/lampepfl/dotty/pull/17323) +- Fix compiler crash in WUnused [#17340](https://github.com/lampepfl/dotty/pull/17340) + +## Opaque Types + +- Delay opaque alias checking until PostTyper [#16644](https://github.com/lampepfl/dotty/pull/16644) + +## Overloading + +- Handle context function arguments in overloading resolution [#16511](https://github.com/lampepfl/dotty/pull/16511) + +## Parser + +- Improve support for Unicode supplementary characters in identifiers and string interpolation (as in Scala 2) [#16278](https://github.com/lampepfl/dotty/pull/16278) +- Require indent after colon at EOL [#16466](https://github.com/lampepfl/dotty/pull/16466) +- Help givens return refined types [#16293](https://github.com/lampepfl/dotty/pull/16293) + +## Pattern Matching + +- Tweak AvoidMap's derivedSelect [#16563](https://github.com/lampepfl/dotty/pull/16563) +- Space: Use RHS of & when refining subtypes [#16573](https://github.com/lampepfl/dotty/pull/16573) +- Freeze constraints in a condition check of maximiseType [#16526](https://github.com/lampepfl/dotty/pull/16526) +- Restrict syntax of typed patterns [#16150](https://github.com/lampepfl/dotty/pull/16150) +- Test case to show that #16252 works with transparent [#16262](https://github.com/lampepfl/dotty/pull/16262) +- Support inline unapplySeq and with leading given parameters [#16358](https://github.com/lampepfl/dotty/pull/16358) +- Handle sealed prefixes in exh checking [#16621](https://github.com/lampepfl/dotty/pull/16621) +- Detect irrefutable quoted patterns [#16674](https://github.com/lampepfl/dotty/pull/16674) +- Patmat: Use less type variables in prefix inference [#16827](https//github.com/lampepfl/dotty/pull/16827) + +## Pickling + +- Allow case classes with up to 254 parameters [#16501](https://github.com/lampepfl/dotty/pull/16501) +- Correctly unpickle Scala 2 private case classes in traits [#16519](https://github.com/lampepfl/dotty/pull/16519) + +## Polyfunctions + +- Fix #9996: Crash with function accepting polymorphic function type with singleton result [#16327](https://github.com/lampepfl/dotty/pull/16327) + +## Quotes + +- Remove contents of inline methods [#16345](https://github.com/lampepfl/dotty/pull/16345) +- Fix errors in explicit type annotations in inline match cases [#16257](https://github.com/lampepfl/dotty/pull/16257) +- Handle macro annotation suspends and crashes [#16509](https://github.com/lampepfl/dotty/pull/16509) +- Fix macro annotations `spliceOwner` [#16513](https://github.com/lampepfl/dotty/pull/16513) +- Fix HK quoted pattern type variables [#16907](https//github.com/lampepfl/dotty/pull/16907) + +## REPL + +- REPL: Fix crash when printing instances of value classes [#16393](https://github.com/lampepfl/dotty/pull/16393) +- Attempt to fix completion crash [#16267](https://github.com/lampepfl/dotty/pull/16267) +- Fix REPL shadowing bug [#16389](https://github.com/lampepfl/dotty/pull/16389) +- Open up for extensibility [#16276](https://github.com/lampepfl/dotty/pull/16276) +- Don't crash if completions throw [#16687](https://github.com/lampepfl/dotty/pull/16687) + +## Reflection + +- Fix reflect typeMembers to return all members [#15033](https://github.com/lampepfl/dotty/pull/15033) +- Deprecate reflect Flags.Static [#16568](https://github.com/lampepfl/dotty/pull/16568) + +## Reporting + +- Suppress follow-on errors for erroneous import qualifiers [#16658](https://github.com/lampepfl/dotty/pull/16658) +- Fix order in which errors are reported for assignment to val [#16660](https://github.com/lampepfl/dotty/pull/16660) +- Fix class name in error message [#16635](https://github.com/lampepfl/dotty/pull/16635) +- Make refined type printing more source compatible [#16303](https://github.com/lampepfl/dotty/pull/16303) +- Add error hint on local inline def used in quotes [#16572](https://github.com/lampepfl/dotty/pull/16572) +- Fix Text wrapping [#16277](https://github.com/lampepfl/dotty/pull/16277) +- Fix #16680 by registering Ident not containing a symbol [#16689](https://github.com/lampepfl/dotty/pull/16689) +- Fix the non-miniphase tree traverser [#16684](https://github.com/lampepfl/dotty/pull/16684) +- Just warn on type ascription on a pattern [#17454](https://github.com/lampepfl/dotty/pull/17454) + +## Scala-JS + +- Fix #14289: Accept Ident refs to `js.native` in native member rhs. [#16185](https://github.com/lampepfl/dotty/pull/16185) + +## Scaladoc + +- Added jpath check to `ClassLikeSupport` getParentsAsTreeSymbolTuples [#16759](https://github.com/lampepfl/dotty/pull/16759) + +## Standard Library + +- Add `CanEqual` instance for `Map` [#15886](https://github.com/lampepfl/dotty/pull/15886) +- Refine `Tuple.Append` return type [#16140](https://github.com/lampepfl/dotty/pull/16140) +- Remove experimental from `Mirror#fromProductTyped` [#16829](https//github.com/lampepfl/dotty/pull/16829) + +## TASTy format + +- Make it a fatal error if erasure cannot resolve a type [#16373](https://github.com/lampepfl/dotty/pull/16373) + +## Tooling + +- Add -Yimports compiler flag [#16218](https://github.com/lampepfl/dotty/pull/16218) +- Allow BooleanSettings to be set with a colon [#16425](https://github.com/lampepfl/dotty/pull/16425) +- Add support for disabling redirected output in the REPL driver for usage in worksheets in the Scala Plugin for IntelliJ IDEA [#16810](https://github.com/lampepfl/dotty/pull/16810) +- Fix #17187: allow patches with same span [#17366](https://github.com/lampepfl/dotty/pull/17366) + +## Transform + +- Avoid stackoverflow in ExplicitOuter [#16381](https://github.com/lampepfl/dotty/pull/16381) +- Make lazy vals run on non-fallback graal image - remove dynamic reflection [#16346](https://github.com/lampepfl/dotty/pull/16346) +- Patch to avoid crash in #16351 [#16354](https://github.com/lampepfl/dotty/pull/16354) +- Don't treat package object's `` methods as package members [#16667](https://github.com/lampepfl/dotty/pull/16667) +- Space: Refine isSubspace property & an example [#16574](https://github.com/lampepfl/dotty/pull/16574) +- Fix static lazy field holder for GraalVM [#16800](https://github.com/lampepfl/dotty/pull/16800) +- Fix race condition in new LazyVals [#16975](https://github.com/lampepfl/dotty/pull/16975) + +## Typer + +- Drop requirement that self types are closed [#16648](https://github.com/lampepfl/dotty/pull/16648) +- Disallow constructor params from appearing in parent types for soundness [#16664](https://github.com/lampepfl/dotty/pull/16664) +- Don't search implicit arguments in singleton type prefix [#16490](https://github.com/lampepfl/dotty/pull/16490) +- Don't rely on isProvisional to determine whether atoms computed [#16489](https://github.com/lampepfl/dotty/pull/16489) +- Support signature polymorphic methods (`MethodHandle` and `VarHandle`) [#16225](https://github.com/lampepfl/dotty/pull/16225) +- Prefer parameterless alternatives during ambiguous overload resolution [#16315](https://github.com/lampepfl/dotty/pull/16315) +- Fix calculation to drop transparent classes [#16344](https://github.com/lampepfl/dotty/pull/16344) +- Test case for issue 16311 [#16317](https://github.com/lampepfl/dotty/pull/16317) +- Skip caching provisional OrType atoms [#16295](https://github.com/lampepfl/dotty/pull/16295) +- Avoid cyclic references due to experimental check when inlining [#16195](https://github.com/lampepfl/dotty/pull/16195) +- Track type variable dependencies to guide instantiation decisions [#16042](https://github.com/lampepfl/dotty/pull/16042) +- Two fixes to constraint solving [#16353](https://github.com/lampepfl/dotty/pull/16353) +- Fix regression in cyclic constraint handling [#16514](https://github.com/lampepfl/dotty/pull/16514) +- Sharpen range approximation for applied types with capture set ranges [#16261](https://github.com/lampepfl/dotty/pull/16261) +- Cut the Gordian Knot: Don't widen unions to transparent [#15642](https://github.com/lampepfl/dotty/pull/15642) +- Fix widening logic to keep instantiation within bounds [#16417](https://github.com/lampepfl/dotty/pull/16417) +- Skip ambiguous reference error when symbols are aliases [#16401](https://github.com/lampepfl/dotty/pull/16401) +- Avoid incorrect simplifications when updating bounds in the constraint [#16410](https://github.com/lampepfl/dotty/pull/16410) +- Take `@targetName` into account when resolving extension methods [#16487](https://github.com/lampepfl/dotty/pull/16487) +- Improve ClassTag handling to avoid invalid ClassTag generation and inference failure [#16492](https://github.com/lampepfl/dotty/pull/16492) +- Fix extracting the elemType of a union of arrays [#16569](https://github.com/lampepfl/dotty/pull/16569) +- Make sure annotations are typed in expression contexts [#16699](https://github.com/lampepfl/dotty/pull/16699) +- Throw a type error when using hk-types in unions or intersections [#16712](https://github.com/lampepfl/dotty/pull/16712) +- Add missing criterion to subtype check [#16889](https://github.com/lampepfl/dotty/pull/16889) +- Fix caching issue caused by incorrect isProvisional check [#16989](https://github.com/lampepfl/dotty/pull/16989) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.2.2..3.3.0` these are: + +``` + 226 Martin Odersky + 106 Szymon Rodziewicz + 81 Dale Wijnand + 56 Nicolas Stucki + 52 Paul Coral + 48 Kamil Szewczyk + 45 Paweł Marks + 28 Florian3k + 28 Yichen Xu + 15 Guillaume Martres + 10 Michał Pałka + 9 Kacper Korban + 8 Fengyun Liu + 7 Chris Birchall + 7 rochala + 6 Sébastien Doeraene + 6 jdudrak + 5 Seth Tisue + 5 Som Snytt + 5 nizhikov + 4 Filip Zybała + 4 Jan Chyb + 4 Michael Pollmeier + 4 Natsu Kagami + 3 Anatolii Kmetiuk + 3 Jamie Thompson + 2 Adrien Piquerez + 2 Alex + 2 Dmitrii Naumenko + 2 Lukas Rytz + 2 Michael Pilquist + 2 Vasil Vasilev + 2 adampauls + 2 yoshinorin + 1 Alexander Slesarenko + 1 Chris Kipp + 1 Guillaume Raffin + 1 Jakub Kozłowski + 1 Jan-Pieter van den Heuvel + 1 Julien Richard-Foy + 1 Kenji Yoshida + 1 Matt Bovel + 1 Mohammad Yousuf Minhaj Zia + 1 Philippus + 1 Szymon R + 1 Tim Spence + 1 s.bazarsadaev + + +``` \ No newline at end of file From 8e94cd382e5a19e8303690ed7ddf97e6b1c32a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 29 May 2023 16:25:59 +0200 Subject: [PATCH 113/134] Add info about 3.3 to source compat doc [Cherry-picked 0fa1c91eb181e695e425bb8a022daf61c49ab214] [Cherry-picked c706cc10c68fddb828448acba5d55469f4e90964] --- docs/_docs/reference/language-versions/source-compatibility.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/_docs/reference/language-versions/source-compatibility.md b/docs/_docs/reference/language-versions/source-compatibility.md index 077f06b2b4db..3e9954a6d55a 100644 --- a/docs/_docs/reference/language-versions/source-compatibility.md +++ b/docs/_docs/reference/language-versions/source-compatibility.md @@ -23,6 +23,9 @@ The default Scala language syntax version currently supported by the Dotty compi - [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. - [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.2`. Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. +- [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: + -[Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. +- [`3.3-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3-migration$.html): the same as `3.3` - [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.2`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. From 8f2bb1095af0fd0475b758e87c7baf2379bd2e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 30 May 2023 00:13:10 -0700 Subject: [PATCH 114/134] Update docs/_docs/reference/language-versions/source-compatibility.md Co-authored-by: Jamie Thompson [Cherry-picked 724340e3bfd5c970655fe6e7f49d2d91697c96fe] [Cherry-picked 49c09521c4bb4893302c2f41310d91a05ed9b890] --- docs/_docs/reference/language-versions/source-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/language-versions/source-compatibility.md b/docs/_docs/reference/language-versions/source-compatibility.md index 3e9954a6d55a..131bb100a91b 100644 --- a/docs/_docs/reference/language-versions/source-compatibility.md +++ b/docs/_docs/reference/language-versions/source-compatibility.md @@ -27,7 +27,7 @@ Some Scala 2 specific idioms are dropped in this version. The feature set suppor -[Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. - [`3.3-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3-migration$.html): the same as `3.3` -- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.2`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. +- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. There are two ways to specify a language version : From 8e9f15c82db3b6a3edff604c9456549d448de45e Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 30 May 2023 16:12:44 +0200 Subject: [PATCH 115/134] Update source-compatibility.md reorder the source versions [Cherry-picked 232180f07a4415863b19f7e87ead852694effaf3] [Cherry-picked a4ef792dc85b87523899bbe68bb281d59a6d82f4] --- .../reference/language-versions/source-compatibility.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/_docs/reference/language-versions/source-compatibility.md b/docs/_docs/reference/language-versions/source-compatibility.md index 131bb100a91b..5cb705a16b82 100644 --- a/docs/_docs/reference/language-versions/source-compatibility.md +++ b/docs/_docs/reference/language-versions/source-compatibility.md @@ -21,12 +21,11 @@ The default Scala language syntax version currently supported by the Dotty compi - [stricter pattern bindings](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html) are now enabled (part of `future` in earlier `3.x` releases), producing warnings for refutable patterns. These warnings can be silenced to achieve the same runtime behavior, but in `future` they become errors and refutable patterns will not compile. - [Nonlocal returns](https://docs.scala-lang.org/scala3/reference/dropped-features/nonlocal-returns.html) now produce a warning upon usage (they are still an error under `future`). - [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. -- [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.2`. -Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. - [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: -[Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. - [`3.3-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3-migration$.html): the same as `3.3` - +- [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.3`. +Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. - [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. There are two ways to specify a language version : From e48cb84cd619d5087ee4919c5143f6b2ee698dc5 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 21 Jun 2023 11:10:59 +0200 Subject: [PATCH 116/134] sort language versions to match natural ordering also fixes the bullet-point under 3.3 to actually be a bullet point [Cherry-picked 38265fca08ff605edc9df99f9628b2959c22a134] [Cherry-picked 1237b165ec1618e6ff33da9bef5da289e18d40e4] --- .../reference/language-versions/source-compatibility.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_docs/reference/language-versions/source-compatibility.md b/docs/_docs/reference/language-versions/source-compatibility.md index 5cb705a16b82..145c4a84d11b 100644 --- a/docs/_docs/reference/language-versions/source-compatibility.md +++ b/docs/_docs/reference/language-versions/source-compatibility.md @@ -17,16 +17,16 @@ The default Scala language syntax version currently supported by the Dotty compi - in conjunction with `-rewrite`, offer code rewrites from Scala 2.13 to 3.0. - [`3.0`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/0$.html), [`3.1`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/1$.html): the default set of features included in scala versions `3.0.0` to `3.1.3`. +- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. - [`3.2`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2$.html): the same as `3.0` and `3.1`, but in addition: - [stricter pattern bindings](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html) are now enabled (part of `future` in earlier `3.x` releases), producing warnings for refutable patterns. These warnings can be silenced to achieve the same runtime behavior, but in `future` they become errors and refutable patterns will not compile. - [Nonlocal returns](https://docs.scala-lang.org/scala3/reference/dropped-features/nonlocal-returns.html) now produce a warning upon usage (they are still an error under `future`). -- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. -- [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: - -[Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. - [`3.3-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3-migration$.html): the same as `3.3` +- [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: + - [Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. +- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. - [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.3`. Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. -- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. There are two ways to specify a language version : From 4ee197153a533e37bdd569b984ed90e7d5466054 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Mon, 26 Jun 2023 16:19:22 +0200 Subject: [PATCH 117/134] Update indentation.md [Cherry-picked 28d207dd9da5112204269d97d869b64e8cce9828] [Cherry-picked 390d414a9172c6a4bd00faffda29570a63e54e4f] --- .../_docs/reference/other-new-features/indentation.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/_docs/reference/other-new-features/indentation.md b/docs/_docs/reference/other-new-features/indentation.md index de73bb67d451..9963d1ee7577 100644 --- a/docs/_docs/reference/other-new-features/indentation.md +++ b/docs/_docs/reference/other-new-features/indentation.md @@ -274,7 +274,8 @@ Indentation can be mixed freely with braces `{...}`, as well as brackets `[...]` For instance, consider: ```scala { - val x = f(x: Int, y => + val x = 4 + f(x: Int, y => x * ( y + 1 ) + @@ -283,13 +284,13 @@ For instance, consider: ) } ``` - - Here, the indentation width of the region enclosed by the braces is 3 (i.e. the indentation width of the + - Here, the indentation width of the region enclosed by the braces is 2 (i.e. the indentation width of the statement starting with `val`). - - The indentation width of the region in parentheses that follows `f` is also 3, since the opening + - The indentation width of the region in parentheses that follows `f` is also 2, since the opening parenthesis is not at the end of a line. - - The indentation width of the region in parentheses around `y + 1` is 9 + - The indentation width of the region in parentheses around `y + 1` is 6 (i.e. the indentation width of `y + 1`). - - Finally, the indentation width of the last region in parentheses starting with `(x` is 6 (i.e. the indentation width of the indented region following the `=>`. + - Finally, the indentation width of the last region in parentheses starting with `(x` is 4 (i.e. the indentation width of the indented region following the `=>`. ## Special Treatment of Case Clauses From 8b952638041a98b5edbaa5f623b900b8597f1505 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 14 Jul 2023 11:42:23 +0200 Subject: [PATCH 118/134] Update link to point to correct section In the reference, in Erased Definitions, link pointed to the Inline page, even though the content is in Compile Time Operations [Cherry-picked c54bf671b0293890a26a21d0b6325ad1a117615d] [Cherry-picked a87132d97b83b484a9e9ebecb0ed90a0be3d30f1] --- docs/_docs/reference/experimental/erased-defs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_docs/reference/experimental/erased-defs.md b/docs/_docs/reference/experimental/erased-defs.md index ef4f02e33dd4..d266cd6c9d19 100644 --- a/docs/_docs/reference/experimental/erased-defs.md +++ b/docs/_docs/reference/experimental/erased-defs.md @@ -161,8 +161,8 @@ object Machine: // State must be Off ``` -Note that in [Inline](../metaprogramming/inline.md) we discussed `erasedValue` and inline -matches. `erasedValue` is implemented with `erased`, so the state machine above +Note that in [Compile-time operations](../metaprogramming/compiletime-ops.md#erasedvalue) we discussed `erasedValue` and inline +matches. `erasedValue` is internally implemented with `erased` (and is not experimental), so the state machine above can be encoded as follows: ```scala From 3aedfdd229c986407822dd9763b1cfd5bb8aab0e Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 21 Jun 2024 19:47:19 +0200 Subject: [PATCH 119/134] Add changelog for 3.3.1-RC2 [Cherry-picked 1451dc50ccc7bdf52501af02052aa1059487e393] [Cherry-picked f5a2d2547713fbb928dc6b555d9851c11b1e4606] From 504740b1fb1cf48606b83ba02ac4dc5ec3a9c778 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 21 Jun 2024 19:47:57 +0200 Subject: [PATCH 120/134] Add changelog for 3.3.1-RC5 [Cherry-picked 5d6891fe3de921a825d53b369cc9b3e805275753] [Cherry-picked ab6448c31728be5adc1f9b2267cc22341fa93e05] From 4d2b07c9a2037a081fa8828bda8a3c04619ad264 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 21 Jun 2024 19:48:36 +0200 Subject: [PATCH 121/134] Add changelog for 3.3.1 Also fix typos in older changelogs [Cherry-picked 9b4ea8eb22dd96c94eb5d5c62cd0c0d277342a53] [Cherry-picked 8166fe99595c82f12159195600ea931f6487698f] From e7dd7b3248c8e9a1a69481552cf8536b3bb5f7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C4=85browski?= Date: Sun, 1 Oct 2023 21:56:51 +0200 Subject: [PATCH 122/134] Fix open-classes.md [Cherry-picked 3eb354bc1e9a47c2e92ba709ef83cb898d60fe83] [Cherry-picked e741946e02cbfabfdff4cdba5a7c9afe015f6e3e] --- docs/_docs/reference/other-new-features/open-classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/other-new-features/open-classes.md b/docs/_docs/reference/other-new-features/open-classes.md index 764c234df599..10af6ead669e 100644 --- a/docs/_docs/reference/other-new-features/open-classes.md +++ b/docs/_docs/reference/other-new-features/open-classes.md @@ -77,4 +77,4 @@ A class that is neither `abstract` nor `open` is similar to a `sealed` class: it ## Migration -`open` is a new modifier in Scala 3. To allow cross compilation between Scala 2.13 and Scala 3.0 without warnings, the feature warning for ad-hoc extensions is produced only under `-source future`. It will be produced by default from Scala 3.1 on. +`open` is a new modifier in Scala 3. To allow cross compilation between Scala 2.13 and Scala 3.0 without warnings, the feature warning for ad-hoc extensions is produced only under `-source future`. It will be produced by default [from Scala 3.4 on](https://github.com/lampepfl/dotty/issues/16334). From b68b34bd6a1d88cda845ad679fcad86615f5f2aa Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 18 Oct 2023 14:11:11 +0200 Subject: [PATCH 123/134] Use constructor's default getters in case class synthetic `apply` methods Fixes #18715 [Cherry-picked ef1458270dc5f84f5bf58dcd8582357a9c99b126] --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 9 +++++++-- tests/pos/i18715.scala | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i18715.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9aad20113154..36ffbd2e64a4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1137,11 +1137,16 @@ class Namer { typer: Typer => def foreachDefaultGetterOf(sym: TermSymbol, op: TermSymbol => Unit): Unit = var n = 0 + val methodName = + if sym.name == nme.apply && sym.is(Synthetic) && sym.owner.companionClass.is(Case) then + // The synthesized `apply` methods of case classes use the constructor's default getters + nme.CONSTRUCTOR + else sym.name for params <- sym.paramSymss; param <- params do if param.isTerm then if param.is(HasDefault) then - val getterName = DefaultGetterName(sym.name, n) - val getter = pathType.member(DefaultGetterName(sym.name, n)).symbol + val getterName = DefaultGetterName(methodName, n) + val getter = pathType.member(getterName).symbol assert(getter.exists, i"$path does not have a default getter named $getterName") op(getter.asTerm) n += 1 diff --git a/tests/pos/i18715.scala b/tests/pos/i18715.scala new file mode 100644 index 000000000000..5023ef211fa3 --- /dev/null +++ b/tests/pos/i18715.scala @@ -0,0 +1,5 @@ +case class Foo(x: Int = 0) + +extension (x: Any) + private def foo = Foo + export foo.apply From d53cbac2778307c32e6fee8b5f01578fc26831cb Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 21 Jun 2024 19:58:23 +0200 Subject: [PATCH 124/134] Better error message when a pattern match extractor is not found. Fixes #18684 [Cherry-picked 7c308d6a7a3cef4ea804623e821250065cfae444][modified] --- .../dotty/tools/dotc/core/TypeErrors.scala | 14 +++++---- .../dotty/tools/dotc/reporting/messages.scala | 10 ++++++- .../dotty/tools/dotc/typer/Applications.scala | 19 ++++++++---- .../src/dotty/tools/dotc/typer/Typer.scala | 22 +++++++++++--- tests/neg/bad-unapplies.check | 8 ++--- tests/neg/i18684.check | 30 +++++++++++++++++++ tests/neg/i18684.scala | 12 ++++++++ tests/neg/i5101.check | 4 +-- tests/neg/t5702-neg-bad-and-wild.check | 8 ++--- 9 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 tests/neg/i18684.check create mode 100644 tests/neg/i18684.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 24a207da6836..512467e9669d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -131,14 +131,18 @@ end handleRecursive * so it requires knowing denot already. * @param denot */ -class CyclicReference private (val denot: SymDenotation)(using Context) extends TypeError: +class CyclicReference(val denot: SymDenotation)(using Context) extends TypeError: var inImplicitSearch: Boolean = false - override def toMessage(using Context): Message = - val cycleSym = denot.symbol + val cycleSym = denot.symbol + + // cycleSym.flags would try completing denot and would fail, but here we can use flagsUNSAFE to detect flags + // set by the parser. + def unsafeFlags = cycleSym.flagsUNSAFE + def isMethod = unsafeFlags.is(Method) + def isVal = !isMethod && cycleSym.isTerm - // cycleSym.flags would try completing denot and would fail, but here we can use flagsUNSAFE to detect flags - // set by the parser. + override def toMessage(using Context): Message = val unsafeFlags = cycleSym.flagsUNSAFE val isMethod = unsafeFlags.is(Method) val isVal = !isMethod && cycleSym.isTerm diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 61840e2dc6d0..9d1337ff2d09 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2340,7 +2340,7 @@ class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends def explain(using Context) = "" } -class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { +class NotAnExtractor(tree: untpd.Tree)(using Context) extends PatternMatchMsg(NotAnExtractorID) { def msg(using Context) = i"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" def explain(using Context) = i"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: @@ -2353,6 +2353,14 @@ class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExt |This mechanism is used for instance in pattern ${hl("case List(x1, ..., xn)")}""" } +class ExtractorNotFound(val name: Name)(using Context) extends NotFoundMsg(ExtractorNotFoundID): + def msg(using Context) = i"no pattern match extractor named $name was found" + def explain(using Context) = + i"""An application $name(...) in a pattern can refer to an extractor + |which defines an unapply or unapplySeq method. Case classes and enum cases + |implicitly define extractors with the name of the class or enum case. + |Here, no extractor named $name was found, so the pattern could not be typed.""" + class MemberWithSameNameAsStatic()(using Context) extends SyntaxMsg(MemberWithSameNameAsStaticID) { def msg(using Context) = i"Companion classes cannot define members with same name as a ${hl("@static")} member" diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 09daa429a08f..d5983da30d56 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1278,15 +1278,22 @@ trait Applications extends Compatibility { /** Report errors buffered in state. * @pre state has errors to report - * If there is a single error stating that "unapply" is not a member, print - * the more informative "notAnExtractor" message instead. + * If the last reported error states that "unapply" is not a member, report + * the more informative `NotAnExtractor` message instead. + * If the last reported error states that the qualifier was not found, report + * the more informative `ExtractorNotFound` message instead. */ def reportErrors(tree: Tree, state: TyperState): Tree = assert(state.reporter.hasErrors) - if saysNotFound(state, nme.unapply) then notAnExtractor(tree) - else - state.reporter.flush() - tree + if saysNotFound(state, nme.unapply) then + notAnExtractor(tree) + else qual match + case qual: Ident if saysNotFound(state, qual.name) => + report.error(ExtractorNotFound(qual.name), tree.srcPos) + tree + case _ => + state.reporter.flush() + tree /** If this is a term ref tree, try to typecheck with its type name. * If this refers to a type alias, follow the alias, and if diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a6ab751e65b5..40aee83c2e4e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3098,6 +3098,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => typedUnadapted(desugar(tree, pt), pt, locked) } + def handleTypeError(ex: TypeError): Tree = ex match + case ex: CyclicReference + if ctx.reporter.errorsReported + && xtree.span.isZeroExtent + && ex.isVal && false => + // Don't report a "recursive val ... needs type" if errors were reported + // previously and the span of the offending tree is empty. In this case, + // it's most likely that this is desugared code, and the error message would + // be redundant and confusing. + xtree.withType(ErrorType(ex.toMessage)) + case _ => + // Use focussed sourcePos since tree might be a large definition + // and a large error span would hide all errors in interior. + // TODO: Not clear that hiding is what we want, actually + errorTree(xtree, ex, xtree.srcPos.focus) + try val ifpt = defn.asContextFunctionType(pt) val result = @@ -3115,10 +3131,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case xtree => typedUnnamed(xtree) simplify(result, pt, locked) - catch case ex: TypeError => errorTree(xtree, ex, xtree.srcPos.focus) - // use focussed sourcePos since tree might be a large definition - // and a large error span would hide all errors in interior. - // TODO: Not clear that hiding is what we want, actually + catch case ex: TypeError => + handleTypeError(ex) } } diff --git a/tests/neg/bad-unapplies.check b/tests/neg/bad-unapplies.check index 44633ca6950a..51e71d8e8949 100644 --- a/tests/neg/bad-unapplies.check +++ b/tests/neg/bad-unapplies.check @@ -7,13 +7,13 @@ | both match arguments (C) | | longer explanation available when compiling with `-explain` --- [E127] Syntax Error: tests/neg/bad-unapplies.scala:23:9 ------------------------------------------------------------- +-- [E127] Pattern Match Error: tests/neg/bad-unapplies.scala:23:9 ------------------------------------------------------ 23 | case B("2") => // error (cannot be used as an extractor) | ^ | B cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method | | longer explanation available when compiling with `-explain` --- [E127] Syntax Error: tests/neg/bad-unapplies.scala:24:9 ------------------------------------------------------------- +-- [E127] Pattern Match Error: tests/neg/bad-unapplies.scala:24:9 ------------------------------------------------------ 24 | case D("2") => // error (cannot be used as an extractor) | ^ | D cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method @@ -31,9 +31,9 @@ | Wrong number of argument patterns for F; expected: () | | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/bad-unapplies.scala:27:9 ---------------------------------------------------------- +-- [E189] Not Found Error: tests/neg/bad-unapplies.scala:27:9 ---------------------------------------------------------- 27 | case G("2") => // error (Not found: G) | ^ - | Not found: G + | no pattern match extractor named G was found | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check new file mode 100644 index 000000000000..69ed47cc2613 --- /dev/null +++ b/tests/neg/i18684.check @@ -0,0 +1,30 @@ +-- [E189] Not Found Error: tests/neg/i18684.scala:3:6 ------------------------------------------------------------------ +3 | val s(): String = "hello, world" // error + | ^ + | no pattern match extractor named s was found + | + | longer explanation available when compiling with `-explain` +-- [E189] Not Found Error: tests/neg/i18684.scala:5:6 ------------------------------------------------------------------ +5 | val i() = 22 // error + | ^ + | no pattern match extractor named i was found + | + | longer explanation available when compiling with `-explain` +-- [E189] Not Found Error: tests/neg/i18684.scala:10:8 ----------------------------------------------------------------- +10 | val foo() = "33" // error + | ^^^ + | no pattern match extractor named foo was found + | + | longer explanation available when compiling with `-explain` +-- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- +12 | val inner(x) = 3 // error // error + | ^^^^^ + | Test.inner cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method + | + | longer explanation available when compiling with `-explain` +-- [E045] Cyclic Error: tests/neg/i18684.scala:12:14 ------------------------------------------------------------------- +12 | val inner(x) = 3 // error // error + | ^ + | Recursive value x needs type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i18684.scala b/tests/neg/i18684.scala new file mode 100644 index 000000000000..48e2d691a304 --- /dev/null +++ b/tests/neg/i18684.scala @@ -0,0 +1,12 @@ +//> using option -explain +object Test: + val s(): String = "hello, world" // error + + val i() = 22 // error + + def foo(): String = "22" + + object inner: + val foo() = "33" // error + + val inner(x) = 3 // error // error \ No newline at end of file diff --git a/tests/neg/i5101.check b/tests/neg/i5101.check index 4f4bac89aa44..c86976398b0e 100644 --- a/tests/neg/i5101.check +++ b/tests/neg/i5101.check @@ -1,6 +1,6 @@ --- [E006] Not Found Error: tests/neg/i5101.scala:11:11 ----------------------------------------------------------------- +-- [E189] Not Found Error: tests/neg/i5101.scala:11:11 ----------------------------------------------------------------- 11 | case A0(_) => // error | ^^ - | Not found: A0 + | no pattern match extractor named A0 was found | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index c461b76ea70b..e2dc41fe2a5e 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -38,16 +38,16 @@ | x is already defined as value x | | Note that overloaded methods must all be defined in the same group of toplevel definitions --- [E006] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:12:20 ------------------------------------------------ +-- [E189] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:12:20 ------------------------------------------------ 12 | case List(1, _*3,) => // error: pattern expected // error | ^ - | Not found: * + | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:13:20 ------------------------------------------------ +-- [E189] Not Found Error: tests/neg/t5702-neg-bad-and-wild.scala:13:20 ------------------------------------------------ 13 | case List(1, _*3:) => // error // error | ^ - | Not found: * + | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` -- [E045] Cyclic Error: tests/neg/t5702-neg-bad-and-wild.scala:23:19 --------------------------------------------------- From 6440434ece26b06f248dd5c1b221cd59781b3205 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 21 Jun 2024 19:58:57 +0200 Subject: [PATCH 125/134] Suppress redundant "recursive value needs type" messages [Cherry-picked aa0df6c094149ea9d21bf3e96626f27583278ee4][modified] --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/neg/i18684.check | 8 +------- tests/neg/i18684.scala | 2 +- tests/neg/t5702-neg-bad-and-wild.check | 8 +------- tests/neg/t5702-neg-bad-and-wild.scala | 2 +- 5 files changed, 5 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 40aee83c2e4e..dfb7fd4a442e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3102,7 +3102,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case ex: CyclicReference if ctx.reporter.errorsReported && xtree.span.isZeroExtent - && ex.isVal && false => + && ex.isVal => // Don't report a "recursive val ... needs type" if errors were reported // previously and the span of the offending tree is empty. In this case, // it's most likely that this is desugared code, and the error message would diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check index 69ed47cc2613..c384570ff798 100644 --- a/tests/neg/i18684.check +++ b/tests/neg/i18684.check @@ -17,14 +17,8 @@ | | longer explanation available when compiling with `-explain` -- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- -12 | val inner(x) = 3 // error // error +12 | val inner(x) = 3 // error | ^^^^^ | Test.inner cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method | | longer explanation available when compiling with `-explain` --- [E045] Cyclic Error: tests/neg/i18684.scala:12:14 ------------------------------------------------------------------- -12 | val inner(x) = 3 // error // error - | ^ - | Recursive value x needs type - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i18684.scala b/tests/neg/i18684.scala index 48e2d691a304..6ddca460814c 100644 --- a/tests/neg/i18684.scala +++ b/tests/neg/i18684.scala @@ -9,4 +9,4 @@ object Test: object inner: val foo() = "33" // error - val inner(x) = 3 // error // error \ No newline at end of file + val inner(x) = 3 // error \ No newline at end of file diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index e2dc41fe2a5e..876a3a39b308 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -27,7 +27,7 @@ | | longer explanation available when compiling with `-explain` -- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:23:17 --------------------------------------------------- -23 | val K(ns @ _*, xx) = k // error: pattern expected // error +23 | val K(ns @ _*, xx) = k // error: pattern expected | ^ | pattern expected | @@ -50,12 +50,6 @@ | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` --- [E045] Cyclic Error: tests/neg/t5702-neg-bad-and-wild.scala:23:19 --------------------------------------------------- -23 | val K(ns @ _*, xx) = k // error: pattern expected // error - | ^ - | Recursive value $1$ needs type - | - | longer explanation available when compiling with `-explain` -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- 13 | case List(1, _*3:) => // error // error | ^ diff --git a/tests/neg/t5702-neg-bad-and-wild.scala b/tests/neg/t5702-neg-bad-and-wild.scala index 95d00c270e89..d9fec80cf493 100644 --- a/tests/neg/t5702-neg-bad-and-wild.scala +++ b/tests/neg/t5702-neg-bad-and-wild.scala @@ -20,7 +20,7 @@ object Test { // good syntax, bad semantics, detected by typer //gowild.scala:14: error: star patterns must correspond with varargs parameters val K(x @ _*) = k - val K(ns @ _*, xx) = k // error: pattern expected // error + val K(ns @ _*, xx) = k // error: pattern expected val K(x) = k // error: x is already defined as value x val (b, _ * ) = (5,6) // ok // no longer complains From 9c0bca39d8cbda6aa70ed646ce30d7e95989b8e7 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 19 Oct 2023 11:22:01 +0200 Subject: [PATCH 126/134] Turn on -explain in test [Cherry-picked 8df6b48bb5e068975182c66806d715a09e3c82ba] --- tests/neg/i18684.check | 42 +++++++++++++++++++++++++++++++++++------- tests/neg/i18684.scala | 2 +- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check index c384570ff798..f3065fa059b7 100644 --- a/tests/neg/i18684.check +++ b/tests/neg/i18684.check @@ -2,23 +2,51 @@ 3 | val s(): String = "hello, world" // error | ^ | no pattern match extractor named s was found - | - | longer explanation available when compiling with `-explain` + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An application s(...) in a pattern can refer to an extractor + | which defines an unapply or unapplySeq method. Case classes and enum cases + | implicitly define extractors with the name of the class or enum case. + | Here, no extractor named s was found, so the pattern could not be typed. + --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:5:6 ------------------------------------------------------------------ 5 | val i() = 22 // error | ^ | no pattern match extractor named i was found - | - | longer explanation available when compiling with `-explain` + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An application i(...) in a pattern can refer to an extractor + | which defines an unapply or unapplySeq method. Case classes and enum cases + | implicitly define extractors with the name of the class or enum case. + | Here, no extractor named i was found, so the pattern could not be typed. + --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:10:8 ----------------------------------------------------------------- 10 | val foo() = "33" // error | ^^^ | no pattern match extractor named foo was found - | - | longer explanation available when compiling with `-explain` + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An application foo(...) in a pattern can refer to an extractor + | which defines an unapply or unapplySeq method. Case classes and enum cases + | implicitly define extractors with the name of the class or enum case. + | Here, no extractor named foo was found, so the pattern could not be typed. + -------------------------------------------------------------------------------------------------------------------- -- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- 12 | val inner(x) = 3 // error | ^^^^^ | Test.inner cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An unapply method should be defined in an object as follow: + | - If it is just a test, return a Boolean. For example case even() + | - If it returns a single sub-value of type T, return an Option[T] + | - If it returns several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)] | - | longer explanation available when compiling with `-explain` + | Sometimes, the number of sub-values isn't fixed and we would like to return a sequence. + | For this reason, you can also define patterns through unapplySeq which returns Option[Seq[T]]. + | This mechanism is used for instance in pattern case List(x1, ..., xn) + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i18684.scala b/tests/neg/i18684.scala index 6ddca460814c..a6d27a1b3d01 100644 --- a/tests/neg/i18684.scala +++ b/tests/neg/i18684.scala @@ -1,4 +1,4 @@ -//> using option -explain +//> using options -explain object Test: val s(): String = "hello, world" // error From 8937d0bcd5b2caa71c57e98f43aa66c4bce69074 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 19 Oct 2023 16:01:06 +0200 Subject: [PATCH 127/134] Add example to explanation [Cherry-picked eed38ec61de24952751eb66d4b456bc317ed314f] --- .../dotty/tools/dotc/reporting/messages.scala | 14 ++++++- tests/neg/i18684.check | 42 ++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 9d1337ff2d09..5da5bd187d69 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2357,8 +2357,18 @@ class ExtractorNotFound(val name: Name)(using Context) extends NotFoundMsg(Extra def msg(using Context) = i"no pattern match extractor named $name was found" def explain(using Context) = i"""An application $name(...) in a pattern can refer to an extractor - |which defines an unapply or unapplySeq method. Case classes and enum cases - |implicitly define extractors with the name of the class or enum case. + |which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + |The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + |`snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + |enum cases implicitly define extractors with the name of the class or enum case. |Here, no extractor named $name was found, so the pattern could not be typed.""" class MemberWithSameNameAsStatic()(using Context) diff --git a/tests/neg/i18684.check b/tests/neg/i18684.check index f3065fa059b7..5dc4a2fdd736 100644 --- a/tests/neg/i18684.check +++ b/tests/neg/i18684.check @@ -6,8 +6,18 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | An application s(...) in a pattern can refer to an extractor - | which defines an unapply or unapplySeq method. Case classes and enum cases - | implicitly define extractors with the name of the class or enum case. + | which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + | The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + | `snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + | enum cases implicitly define extractors with the name of the class or enum case. | Here, no extractor named s was found, so the pattern could not be typed. --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:5:6 ------------------------------------------------------------------ @@ -18,8 +28,18 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | An application i(...) in a pattern can refer to an extractor - | which defines an unapply or unapplySeq method. Case classes and enum cases - | implicitly define extractors with the name of the class or enum case. + | which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + | The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + | `snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + | enum cases implicitly define extractors with the name of the class or enum case. | Here, no extractor named i was found, so the pattern could not be typed. --------------------------------------------------------------------------------------------------------------------- -- [E189] Not Found Error: tests/neg/i18684.scala:10:8 ----------------------------------------------------------------- @@ -30,8 +50,18 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | An application foo(...) in a pattern can refer to an extractor - | which defines an unapply or unapplySeq method. Case classes and enum cases - | implicitly define extractors with the name of the class or enum case. + | which defines an unapply or unapplySeq method. Example: + | + | object split: + | def unapply(x: String) = + | val (leading, trailing) = x.splitAt(x.length / 2) + | Some((leading, trailing)) + | + | val split(fst, snd) = "HiHo" + | + | The extractor pattern `split(fst, snd)` defines `fst` as the first half "Hi" and + | `snd` as the second half "Ho" of the right hand side "HiHo". Case classes and + | enum cases implicitly define extractors with the name of the class or enum case. | Here, no extractor named foo was found, so the pattern could not be typed. -------------------------------------------------------------------------------------------------------------------- -- [E127] Pattern Match Error: tests/neg/i18684.scala:12:6 ------------------------------------------------------------- From 1af99652dfb529f92b4af67ed993fd29e79411fd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 10:26:47 +0100 Subject: [PATCH 128/134] Vulpix: Remove unused logStackTrace & downstream usages [Cherry-picked 9c92162fdc5dfe3edb9cfa744158e1f6e6050b8a] --- .../dotty/tools/dotc/reporting/TestReporter.scala | 11 ----------- .../test/dotty/tools/vulpix/ParallelTesting.scala | 9 +++------ 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala index c0ece68e3b46..03b61c393d35 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala @@ -32,9 +32,6 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M protected final val _consoleReporter = new ConsoleReporter(null, new PrintWriter(_consoleBuf)) final def consoleOutput: String = _consoleBuf.toString - private var _didCrash = false - final def compilerCrashed: Boolean = _didCrash - private var _skip: Boolean = false final def setSkip(): Unit = _skip = true final def skipped: Boolean = _skip @@ -50,14 +47,6 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M def log(msg: String) = _messageBuf.append(msg) - def logStackTrace(thrown: Throwable): Unit = { - _didCrash = true - val sw = new java.io.StringWriter - val pw = new java.io.PrintWriter(sw) - thrown.printStackTrace(pw) - log(sw.toString) - } - /** Prints the message with the given position indication. */ def printMessageAndPos(dia: Diagnostic, extra: String)(using Context): Unit = { val msg = messageAndPos(dia) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 4e6fe67aec37..b9586faf01ce 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -244,7 +244,7 @@ trait ParallelTesting extends RunnerOrchestration { self => final def countErrors (reporters: Seq[TestReporter]) = countErrorsAndWarnings(reporters)._1 final def countWarnings(reporters: Seq[TestReporter]) = countErrorsAndWarnings(reporters)._2 - final def reporterFailed(r: TestReporter) = r.compilerCrashed || r.errorCount > 0 + final def reporterFailed(r: TestReporter) = r.errorCount > 0 /** * For a given test source, returns a check file against which the result of the test run @@ -737,8 +737,7 @@ trait ParallelTesting extends RunnerOrchestration { self => def showDiagnostics = "-> following the diagnostics:\n" + reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "") Option: - if reporters.exists(_.compilerCrashed) then s"Compiler crashed when compiling: ${testSource.title}" - else if reporters.exists(_.errorCount > 0) then + if reporters.exists(_.errorCount > 0) then s"""Compilation failed for: ${testSource.title} |$showDiagnostics |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") @@ -862,7 +861,6 @@ trait ParallelTesting extends RunnerOrchestration { self => override def suppressErrors = true override def maybeFailureMessage(testSource: TestSource, reporters: Seq[TestReporter]): Option[String] = - def compilerCrashed = reporters.exists(_.compilerCrashed) lazy val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq) lazy val actualErrors = reporters.foldLeft(0)(_ + _.errorCount) lazy val (expected, unexpected) = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors)) @@ -871,8 +869,7 @@ trait ParallelTesting extends RunnerOrchestration { self => reporters.flatMap(_.allErrors.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "") Option { - if compilerCrashed then s"Compiler crashed when compiling: ${testSource.title}" - else if actualErrors == 0 then s"\nNo errors found when compiling neg test $testSource" + if actualErrors == 0 then s"\nNo errors found when compiling neg test $testSource" else if expectedErrors == 0 then s"\nNo errors expected/defined in $testSource -- use // error or // nopos-error" else if expectedErrors != actualErrors then s"""|Wrong number of errors encountered when compiling $testSource From 1877e64fc347183409366582be644682a955110f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 10:26:59 +0100 Subject: [PATCH 129/134] Vulpix: Allow warn tests to have no warnings [Cherry-picked b84333234c3f2705824a4989edc5f7e86db1defa] --- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index b9586faf01ce..925c9b4f4d5c 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -741,8 +741,6 @@ trait ParallelTesting extends RunnerOrchestration { self => s"""Compilation failed for: ${testSource.title} |$showDiagnostics |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") - else if obtCount == 0 then s"\nNo warnings found when compiling warn test $testSource" - else if expCount == 0 then s"\nNo warning expected/defined in $testSource -- use // warn" else if expCount != obtCount then s"""|Wrong number of warnings encountered when compiling $testSource |expected: $expCount, actual: $obtCount From 3cee2542686b3d21c14087327799e8c3795fc97b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 10:35:30 +0100 Subject: [PATCH 130/134] Vulpix: Skip empty lines [Cherry-picked 372e48595d341186e2e6d6b1f20294c2c5adfe7a] --- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 925c9b4f4d5c..8d07002ac620 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -733,9 +733,10 @@ trait ParallelTesting extends RunnerOrchestration { self => lazy val (map, expCount) = getWarnMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq) lazy val obtCount = reporters.foldLeft(0)(_ + _.warningCount) lazy val (expected, unexpected) = getMissingExpectedWarnings(map, reporters.iterator.flatMap(_.diagnostics)) + lazy val diagnostics = reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s" at ${e.pos.line + 1}: ${e.message}")) + def showLines(title: String, lines: Seq[String]) = if lines.isEmpty then "" else title + lines.mkString("\n", "\n", "") def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty - def showDiagnostics = "-> following the diagnostics:\n" + - reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s"${e.pos.line + 1}: ${e.message}")).mkString(" at ", "\n at ", "") + def showDiagnostics = showLines("-> following the diagnostics:", diagnostics) Option: if reporters.exists(_.errorCount > 0) then s"""Compilation failed for: ${testSource.title} @@ -744,8 +745,8 @@ trait ParallelTesting extends RunnerOrchestration { self => else if expCount != obtCount then s"""|Wrong number of warnings encountered when compiling $testSource |expected: $expCount, actual: $obtCount - |${expected.mkString("Unfulfilled expectations:\n", "\n", "")} - |${unexpected.mkString("Unexpected warnings:\n", "\n", "")} + |${showLines("Unfulfilled expectations:", expected)} + |${showLines("Unexpected warnings:", unexpected)} |$showDiagnostics |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") else if hasMissingAnnotations then s"\nWarnings found on incorrect row numbers when compiling $testSource\n$showDiagnostics" From 9cd5fc24f5fe4312b46ff41a7ca85c368e18cc79 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 17 Oct 2023 10:41:00 +0100 Subject: [PATCH 131/134] Fix unreachable warning in deeply nested sealed hierarchy Without applying we were constructing the type Jacket#Body, instead of Jacket[?]#Body, which lead down to an incorrect isSubSpace calculation and thus an unreachable warning. [Cherry-picked 9460b7d4fb8c2e7ee197831b76d458f2f67cbf71] --- .../src/dotty/tools/dotc/core/TypeOps.scala | 2 +- tests/warn/i18661.scala | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/warn/i18661.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 6809e4b9083c..6478e7332e4b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -883,7 +883,7 @@ object TypeOps: else if symbol.is(Module) then TermRef(this(tref.prefix), symbol.sourceModule) else if (prefixTVar != null) - this(tref) + this(tref.applyIfParameterized(tref.typeParams.map(_ => WildcardType))) else { prefixTVar = WildcardType // prevent recursive call from assigning it // e.g. tests/pos/i15029.more.scala, create a TypeVar for `Instances`' B, so we can disregard `Ints` diff --git a/tests/warn/i18661.scala b/tests/warn/i18661.scala new file mode 100644 index 000000000000..b7f490ae44df --- /dev/null +++ b/tests/warn/i18661.scala @@ -0,0 +1,28 @@ +class Jacket[T]: + sealed trait BodyType: + sealed trait OrganType: + case class Heart() extends Body.Organ + case class Brain() extends Body.Organ + object Organ extends OrganType + sealed trait Organ + object Body extends BodyType + sealed trait Body + +type AnyJacket = Jacket[?] +type AnyBodyOrgan = AnyJacket#BodyType#Organ +type AnyBodyOrganHeart = AnyJacket#BodyType#OrganType#Heart +type AnyBodyOrganBrain = AnyJacket#BodyType#OrganType#Brain + +def check( asr : AnyBodyOrgan ) : String = + asr match + case c : AnyBodyOrganHeart => "Heart" + case s : AnyBodyOrganBrain => "Brain" // was: unreachable + +val jacket = new Jacket[Unit] +val heart = new jacket.Body.Organ.Heart() +val brain = new jacket.Body.Organ.Brain() + +@main +def go = + println( check( heart ) ) + println( check( brain ) ) From 14f3e0383b9e8fe967629fd70b33462e9cdc8334 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 19 Oct 2023 10:56:45 +0200 Subject: [PATCH 132/134] Properly dealias tuple types when specializing Fixes #18638 [Cherry-picked 2701a2d3390ff344c8d34c17aa86dfa8a18662a1] --- .../dotc/transform/SpecializeTuples.scala | 8 ++++---- tests/run/i18638.scala | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/run/i18638.scala diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala index 5237ecbcef8a..1013a6f88ab2 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala @@ -37,10 +37,10 @@ class SpecializeTuples extends MiniPhase: end transformApply override def transformSelect(tree: Select)(using Context): Tree = tree match - case Select(qual, nme._1) if isAppliedSpecializableTuple(qual.tpe.widen) => - Select(qual, nme._1.specializedName(qual.tpe.widen.argInfos.slice(0, 1))) - case Select(qual, nme._2) if isAppliedSpecializableTuple(qual.tpe.widen) => - Select(qual, nme._2.specializedName(qual.tpe.widen.argInfos.slice(1, 2))) + case Select(qual, nme._1) if isAppliedSpecializableTuple(qual.tpe.widenDealias) => + Select(qual, nme._1.specializedName(qual.tpe.widenDealias.argInfos.slice(0, 1))) + case Select(qual, nme._2) if isAppliedSpecializableTuple(qual.tpe.widenDealias) => + Select(qual, nme._2.specializedName(qual.tpe.widenDealias.argInfos.slice(1, 2))) case _ => tree private def isAppliedSpecializableTuple(tp: Type)(using Context) = tp match diff --git a/tests/run/i18638.scala b/tests/run/i18638.scala new file mode 100644 index 000000000000..87877b5ba4e7 --- /dev/null +++ b/tests/run/i18638.scala @@ -0,0 +1,18 @@ +type U[H, T] = (Unit, Unit) +object O: + opaque type U[H, T] <: (Unit, Unit) = (Unit, Unit) + def u: U[Int, Int] = ((), ()) + + +def test1(u: (Unit, Unit)) = u._1 +def test2(u: U[Int, Int]) = u._1 +def test3(u: O.U[Int, Int]) = u._1 +def test4() = + (((), ()): U[Int, Int]) match + case ((), ()) => println("ok") + +@main def Test: Unit = + test1(((), ())) + test2(((), ())) + test3(O.u) + test4() From 1e615a4eaf45ea8416d348e5b34522fa22ec8598 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 19 Oct 2023 11:10:03 +0200 Subject: [PATCH 133/134] Optimize `SpecializeTuples.transformSelect` [Cherry-picked 22b273f0d4feb01bfee4d0764e427aa348f4a6a4] --- .../tools/dotc/transform/SpecializeTuples.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala index 1013a6f88ab2..264659930505 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala @@ -37,15 +37,14 @@ class SpecializeTuples extends MiniPhase: end transformApply override def transformSelect(tree: Select)(using Context): Tree = tree match - case Select(qual, nme._1) if isAppliedSpecializableTuple(qual.tpe.widenDealias) => - Select(qual, nme._1.specializedName(qual.tpe.widenDealias.argInfos.slice(0, 1))) - case Select(qual, nme._2) if isAppliedSpecializableTuple(qual.tpe.widenDealias) => - Select(qual, nme._2.specializedName(qual.tpe.widenDealias.argInfos.slice(1, 2))) + case Select(qual, name @ (nme._1 | nme._2)) => + qual.tpe.widenDealias match + case AppliedType(tycon, args) if defn.isSpecializableTuple(tycon.classSymbol, args) => + val argIdx = if name == nme._1 then 0 else 1 + Select(qual, name.specializedName(args(argIdx) :: Nil)) + case _ => + tree case _ => tree - - private def isAppliedSpecializableTuple(tp: Type)(using Context) = tp match - case AppliedType(tycon, args) => defn.isSpecializableTuple(tycon.classSymbol, args) - case _ => false end SpecializeTuples object SpecializeTuples: From ff249a7fced48211762c4a4aab8beb078e4fcfd0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 16 Oct 2023 08:46:06 +0200 Subject: [PATCH 134/134] Improve assertion error message for `Apply` and `TypeApply` [Cherry-picked d2b97e74a4fa76cac4cc88ef6fdefd0704562039] --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 23ce290665da..72e9e58eefad 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -45,19 +45,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Apply(fn: Tree, args: List[Tree])(using Context): Apply = fn match case Block(Nil, expr) => Apply(expr, args) - case _: RefTree | _: GenericApply | _: Inlined | _: Hole => - ta.assignType(untpd.Apply(fn, args), fn, args) case _ => - assert(ctx.reporter.errorsReported) + assert( + fn.isInstanceOf[RefTree | GenericApply | Inlined | Hole] || ctx.reporter.errorsReported, + s"Illegal Apply function prefix: $fn" + ) ta.assignType(untpd.Apply(fn, args), fn, args) def TypeApply(fn: Tree, args: List[Tree])(using Context): TypeApply = fn match case Block(Nil, expr) => TypeApply(expr, args) - case _: RefTree | _: GenericApply => - ta.assignType(untpd.TypeApply(fn, args), fn, args) case _ => - assert(ctx.reporter.errorsReported) + assert( + fn.isInstanceOf[RefTree | GenericApply] || ctx.reporter.errorsReported, + s"Illegal TypeApply function prefix: $fn" + ) ta.assignType(untpd.TypeApply(fn, args), fn, args) def Literal(const: Constant)(using Context): Literal =