From 5cc4a9cb1efea46c33322e8f01fffaf02245de69 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 01/97] Add changelog for 3.3.0-RC1 --- 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 5522929ff3c45741899f9f3c01a7b789eb178023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 26 Jan 2023 15:19:30 +0100 Subject: [PATCH 02/97] Fix incorrect TASTy version --- tasty/src/dotty/tools/tasty/TastyFormat.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index ded313fb171c..2d18923e1b0c 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -289,7 +289,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 2 + final val MinorVersion: Int = 3 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing @@ -305,7 +305,7 @@ object TastyFormat { * is able to read final TASTy documents if the file's * `MinorVersion` is strictly less than the current value. */ - final val ExperimentalVersion: Int = 0 + final val ExperimentalVersion: Int = 1 /**This method implements a binary relation (`<:<`) between two TASTy versions. * From 57a6de25f532c8ac6d7ed2ee1ee067e0599d524e 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 03/97] Add changelog for 3.3.0-RC2 --- 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 8dbc9051840e8962f49f42500bf6769d1294d4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 26 Jan 2023 15:25:20 +0100 Subject: [PATCH 04/97] Release 3.3.0-RC2 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 9babd3c9c679..75d3e12baf66 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.2.2" - val baseVersion = "3.3.0-RC1" + val baseVersion = "3.3.0-RC2" // Versions used by the vscode extension to create a new project // This should be the latest published releases. From c188f1dc96361bc8a3dcc494efffb55e53938d1b Mon Sep 17 00:00:00 2001 From: Mohammad Yousuf Minhaj Zia Date: Wed, 25 Jan 2023 00:48:25 +0800 Subject: [PATCH 05/97] Added jpath check to `ClassLikeSupport` getParentsAsTreeSymbolTuples Fixes #15927 Check for whether the non-scala3 parent exists before checking the start and end of the span to confirm whether the span exists in getParentsAsTreeSymbolTuples. --- scaladoc-testcases/src/tests/nonScala3Parent.scala | 13 +++++++++++++ .../tools/scaladoc/tasty/ClassLikeSupport.scala | 3 ++- .../TranslatableSignaturesTestCases.scala | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 scaladoc-testcases/src/tests/nonScala3Parent.scala diff --git a/scaladoc-testcases/src/tests/nonScala3Parent.scala b/scaladoc-testcases/src/tests/nonScala3Parent.scala new file mode 100644 index 000000000000..91183d25b583 --- /dev/null +++ b/scaladoc-testcases/src/tests/nonScala3Parent.scala @@ -0,0 +1,13 @@ +package tests +package nonScala3Parent + +import javax.swing.JPanel +import javax.swing.JFrame + +// https://github.com/lampepfl/dotty/issues/15927 + +trait Foo1 extends Numeric[Any] +trait Foo2 extends JPanel +trait Foo3 extends JFrame +trait Foo4 extends Ordering[Any] +trait Foo5 extends Enumeration diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 920621b8577c..38cc90330265 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -266,7 +266,8 @@ trait ClassLikeSupport: def getParentsAsTreeSymbolTuples: List[(Tree, Symbol)] = if noPosClassDefs.contains(c.symbol) then Nil else for - parentTree <- c.parents if parentTree.pos.start != parentTree.pos.end // We assume here that order is correct + // TODO: add exists function to position methods in Quotes and replace the condition here for checking the JPath + parentTree <- c.parents if parentTree.pos.sourceFile.getJPath.isDefined && parentTree.pos.start != parentTree.pos.end // We assume here that order is correct parentSymbol = parentTree match case t: TypeTree => t.tpe.typeSymbol case tree if tree.symbol.isClassConstructor => tree.symbol.owner diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index ab7c2189e5d5..49316b08dbc0 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -106,3 +106,5 @@ class ImplicitMembers extends SignatureTest( Seq("def"), filterFunc = _.toString.endsWith("OuterClass$ImplicitMemberTarget.html") ) + +class NonScala3Parent extends SignatureTest("nonScala3Parent", SignatureTest.all) From 92c5dade99f41cb820bcefe946cc21e0eaf6934e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 30 Dec 2022 17:58:05 +0000 Subject: [PATCH 06/97] Split out immutable GadtConstraint --- .../dotty/tools/dotc/core/Constraint.scala | 3 + .../tools/dotc/core/ConstraintHandling.scala | 11 +- .../src/dotty/tools/dotc/core/Contexts.scala | 10 +- .../tools/dotc/core/GadtConstraint.scala | 351 +++++++++--------- .../tools/dotc/core/OrderingConstraint.scala | 11 + .../dotc/core/PatternTypeConstrainer.scala | 2 +- .../dotty/tools/dotc/core/TypeComparer.scala | 26 +- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 8 +- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- 10 files changed, 213 insertions(+), 215 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala index b849c7aa7093..c634f847e510 100644 --- a/compiler/src/dotty/tools/dotc/core/Constraint.scala +++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala @@ -71,6 +71,9 @@ abstract class Constraint extends Showable { */ def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds + /** The current bounds of type parameter `param` */ + def bounds(param: TypeParamRef)(using Context): TypeBounds + /** A new constraint which is derived from this constraint by adding * entries for all type parameters of `poly`. * @param tvars A list of type variables associated with the params, diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 6207e0a3d728..9ffe2bda73cb 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -749,16 +749,7 @@ trait ConstraintHandling { } /** The current bounds of type parameter `param` */ - def bounds(param: TypeParamRef)(using Context): TypeBounds = { - val e = constraint.entry(param) - if (e.exists) e.bounds - else { - // TODO: should we change the type of paramInfos to nullable? - val pinfos: List[param.binder.PInfo] | Null = param.binder.paramInfos - if (pinfos != null) pinfos(param.paramNum) // pinfos == null happens in pos/i536.scala - else TypeBounds.empty - } - } + def bounds(param: TypeParamRef)(using Context): TypeBounds = constraint.bounds(param) /** Add type lambda `tl`, possibly with type variables `tvars`, to current constraint * and propagate all bounds. diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 5f82e8c8b6ce..398af92e494d 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -141,7 +141,7 @@ object Contexts { def tree: Tree[?] def scope: Scope def typerState: TyperState - def gadt: GadtConstraint + def gadt: GadtConstraintHandling def searchHistory: SearchHistory def source: SourceFile @@ -541,8 +541,8 @@ object Contexts { private var _typerState: TyperState = uninitialized final def typerState: TyperState = _typerState - private var _gadt: GadtConstraint = uninitialized - final def gadt: GadtConstraint = _gadt + private var _gadt: GadtConstraintHandling = uninitialized + final def gadt: GadtConstraintHandling = _gadt private var _searchHistory: SearchHistory = uninitialized final def searchHistory: SearchHistory = _searchHistory @@ -624,7 +624,7 @@ object Contexts { this._scope = typer.scope setTypeAssigner(typer) - def setGadt(gadt: GadtConstraint): this.type = + def setGadt(gadt: GadtConstraintHandling): this.type = util.Stats.record("Context.setGadt") this._gadt = gadt this @@ -721,7 +721,7 @@ object Contexts { .updated(notNullInfosLoc, Nil) .updated(compilationUnitLoc, NoCompilationUnit) c._searchHistory = new SearchRoot - c._gadt = GadtConstraint.empty + c._gadt = GadtConstraintHandling(GadtConstraint.empty) c end FreshContext diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 7515898a36df..d683c15a9241 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -2,34 +2,145 @@ package dotty.tools package dotc package core -import Decorators._ -import Contexts._ -import Types._ -import Symbols._ +import Contexts.*, Decorators.*, Symbols.*, Types.* +import config.Printers.{gadts, gadtsConstr} import util.{SimpleIdentitySet, SimpleIdentityMap} -import collection.mutable import printing._ +import scala.annotation.tailrec +import scala.annotation.internal.sharable +import scala.collection.mutable + object GadtConstraint: - def apply(): GadtConstraint = empty - def empty: GadtConstraint = - new ProperGadtConstraint(OrderingConstraint.empty, SimpleIdentityMap.empty, SimpleIdentityMap.empty, false) + @sharable val empty: GadtConstraint = + GadtConstraint(OrderingConstraint.empty, SimpleIdentityMap.empty, SimpleIdentityMap.empty, false) /** Represents GADT constraints currently in scope */ -sealed trait GadtConstraint ( - private var myConstraint: Constraint, - private var mapping: SimpleIdentityMap[Symbol, TypeVar], - private var reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol], - private var wasConstrained: Boolean -) extends Showable { - this: ConstraintHandling => +class GadtConstraint private ( + private val myConstraint: Constraint, + private val mapping: SimpleIdentityMap[Symbol, TypeVar], + private val reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol], + private val wasConstrained: Boolean, +) extends Showable: + def constraint: Constraint = myConstraint + def symbols: List[Symbol] = mapping.keys + def withConstraint(c: Constraint) = copy(myConstraint = c) + def withWasConstrained = copy(wasConstrained = true) + + def add(sym: Symbol, tv: TypeVar): GadtConstraint = copy( + mapping = mapping.updated(sym, tv), + reverseMapping = reverseMapping.updated(tv.origin, sym), + ) + + /** Is `sym1` ordered to be less than `sym2`? */ + def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = + constraint.isLess(tvarOrError(sym1).origin, tvarOrError(sym2).origin) + + /** Full bounds of `sym`, including TypeRefs to other lower/upper symbols. + * + * @note this performs subtype checks between ordered symbols. + * Using this in isSubType can lead to infinite recursion. Consider `bounds` instead. + */ + def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = mapping(sym) match + case null => null + case tv: TypeVar => fullBounds(tv.origin) // .ensuring(containsNoInternalTypes(_)) + + /** Immediate bounds of `sym`. Does not contain lower/upper symbols (see [[fullBounds]]). */ + def bounds(sym: Symbol)(using Context): TypeBounds | Null = + mapping(sym) match + case null => null + case tv: TypeVar => + def retrieveBounds: TypeBounds = externalize(constraint.bounds(tv.origin)).bounds + retrieveBounds + //.showing(i"gadt bounds $sym: $result", gadts) + //.ensuring(containsNoInternalTypes(_)) + + /** Is the symbol registered in the constraint? + * + * @note this is true even if the symbol is constrained to be equal to another type, unlike [[Constraint.contains]]. + */ + def contains(sym: Symbol)(using Context): Boolean = mapping(sym) != null + + /** GADT constraint narrows bounds of at least one variable */ + def isNarrowing: Boolean = wasConstrained + + def fullBounds(param: TypeParamRef)(using Context): TypeBounds = + nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param)) + + def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = + externalize(constraint.nonParamBounds(param)).bounds + + def fullLowerBound(param: TypeParamRef)(using Context): Type = + constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { + (t, u) => t | externalize(u) + } + + def fullUpperBound(param: TypeParamRef)(using Context): Type = + constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (t, u) => + val eu = externalize(u) + // Any as the upper bound means "no bound", but if F is higher-kinded, + // Any & F = F[_]; this is wrong for us so we need to short-circuit + if t.isAny then eu else t & eu + } + + def externalize(tp: Type, theMap: TypeMap | Null = null)(using Context): Type = tp match + case param: TypeParamRef => reverseMapping(param) match + case sym: Symbol => sym.typeRef + case null => param + case tp: TypeAlias => tp.derivedAlias(externalize(tp.alias, theMap)) + case tp => (if theMap == null then ExternalizeMap() else theMap).mapOver(tp) + + private class ExternalizeMap(using Context) extends TypeMap: + def apply(tp: Type): Type = externalize(tp, this)(using mapCtx) + + def tvarOrError(sym: Symbol)(using Context): TypeVar = + mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN + + @tailrec final def stripInternalTypeVar(tp: Type): Type = tp match + case tv: TypeVar => + val inst = constraint.instType(tv) + if inst.exists then stripInternalTypeVar(inst) else tv + case _ => tp + + def internalize(tp: Type)(using Context): Type = tp match + case nt: NamedType => + val ntTvar = mapping(nt.symbol) + if ntTvar == null then tp + else ntTvar + case _ => tp + + private def containsNoInternalTypes(tp: Type, theAcc: TypeAccumulator[Boolean] | Null = null)(using Context): Boolean = tp match { + case tpr: TypeParamRef => !reverseMapping.contains(tpr) + case tv: TypeVar => !reverseMapping.contains(tv.origin) + case tp => + (if (theAcc != null) theAcc else new ContainsNoInternalTypesAccumulator()).foldOver(true, tp) + } - import dotty.tools.dotc.config.Printers.{gadts, gadtsConstr} + private class ContainsNoInternalTypesAccumulator(using Context) extends TypeAccumulator[Boolean] { + override def apply(x: Boolean, tp: Type): Boolean = x && containsNoInternalTypes(tp, this) + } - private[core] def getConstraint: Constraint = constraint - private[core] def getMapping: SimpleIdentityMap[Symbol, TypeVar] = mapping - private[core] def getReverseMapping: SimpleIdentityMap[TypeParamRef, Symbol] = reverseMapping - private[core] def getWasConstrained: Boolean = wasConstrained + override def toText(printer: Printer): Texts.Text = printer.toText(this) + + /** Provides more information than toText, by showing the underlying Constraint details. */ + def debugBoundsDescription(using Context): String = i"$this\n$constraint" + + private def copy( + myConstraint: Constraint = myConstraint, + mapping: SimpleIdentityMap[Symbol, TypeVar] = mapping, + reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol] = reverseMapping, + wasConstrained: Boolean = wasConstrained, + ): GadtConstraint = GadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) +end GadtConstraint + +object GadtConstraintHandling: + def apply(gadt: GadtConstraint): GadtConstraintHandling = new ProperGadtConstraintHandling(gadt) + +sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { + this: ConstraintHandling => + + def gadt: GadtConstraint = myGadt + private def gadt_=(g: GadtConstraint) = myGadt = g /** Exposes ConstraintHandling.subsumes */ def subsumes(left: GadtConstraint, right: GadtConstraint, pre: GadtConstraint)(using Context): Boolean = { @@ -57,22 +168,19 @@ sealed trait GadtConstraint ( // and used as orderings. def substDependentSyms(tp: Type, isUpper: Boolean)(using Context): Type = { def loop(tp: Type) = substDependentSyms(tp, isUpper) - tp match { + tp match case tp @ AndType(tp1, tp2) if !isUpper => tp.derivedAndType(loop(tp1), loop(tp2)) case tp @ OrType(tp1, tp2) if isUpper => tp.derivedOrType(loop(tp1), loop(tp2)) case tp: NamedType => - params.indexOf(tp.symbol) match { + params.indexOf(tp.symbol) match case -1 => - mapping(tp.symbol) match { + gadt.internalize(tp) match case tv: TypeVar => tv.origin - case null => tp - } + case _ => tp case i => pt.paramRefs(i) - } case tp => tp - } } val tb = param.info.bounds @@ -86,205 +194,92 @@ sealed trait GadtConstraint ( val tvars = params.lazyZip(poly1.paramRefs).map { (sym, paramRef) => val tv = TypeVar(paramRef, creatorState = null) - mapping = mapping.updated(sym, tv) - reverseMapping = reverseMapping.updated(tv.origin, sym) + gadt = gadt.add(sym, tv) tv } // The replaced symbols are picked up here. addToConstraint(poly1, tvars) - .showing(i"added to constraint: [$poly1] $params%, % gadt = $this", gadts) + .showing(i"added to constraint: [$poly1] $params%, % gadt = $gadt", gadts) } /** Further constrain a symbol already present in the constraint. */ def addBound(sym: Symbol, bound: Type, isUpper: Boolean)(using Context): Boolean = { - @annotation.tailrec def stripInternalTypeVar(tp: Type): Type = tp match { - case tv: TypeVar => - val inst = constraint.instType(tv) - if (inst.exists) stripInternalTypeVar(inst) else tv - case _ => tp - } - - val symTvar: TypeVar = stripInternalTypeVar(tvarOrError(sym)) match { + val symTvar: TypeVar = gadt.stripInternalTypeVar(gadt.tvarOrError(sym)) match case tv: TypeVar => tv case inst => gadts.println(i"instantiated: $sym -> $inst") - return if (isUpper) isSub(inst, bound) else isSub(bound, inst) - } + return if isUpper then isSub(inst, bound) else isSub(bound, inst) - val internalizedBound = bound match { - case nt: NamedType => - val ntTvar = mapping(nt.symbol) - if (ntTvar != null) stripInternalTypeVar(ntTvar) else bound - case _ => bound - } + val internalizedBound = gadt.stripInternalTypeVar(gadt.internalize(bound)) val saved = constraint val result = internalizedBound match case boundTvar: TypeVar => - if (boundTvar eq symTvar) true - else if (isUpper) addLess(symTvar.origin, boundTvar.origin) + if boundTvar eq symTvar then true + else if isUpper + then addLess(symTvar.origin, boundTvar.origin) else addLess(boundTvar.origin, symTvar.origin) case bound => addBoundTransitively(symTvar.origin, bound, isUpper) gadts.println { - val descr = if (isUpper) "upper" else "lower" - val op = if (isUpper) "<:" else ">:" + val descr = if isUpper then "upper" else "lower" + val op = if isUpper then "<:" else ">:" i"adding $descr bound $sym $op $bound = $result" } - if constraint ne saved then wasConstrained = true + if constraint ne saved then gadt = gadt.withWasConstrained result } - /** Is `sym1` ordered to be less than `sym2`? */ - def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = - constraint.isLess(tvarOrError(sym1).origin, tvarOrError(sym2).origin) - - /** Full bounds of `sym`, including TypeRefs to other lower/upper symbols. - * - * @note this performs subtype checks between ordered symbols. - * Using this in isSubType can lead to infinite recursion. Consider `bounds` instead. - */ - def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = - mapping(sym) match { - case null => null - // TODO: Improve flow typing so that ascription becomes redundant - case tv: TypeVar => - fullBounds(tv.origin) - // .ensuring(containsNoInternalTypes(_)) - } - - /** Immediate bounds of `sym`. Does not contain lower/upper symbols (see [[fullBounds]]). */ - def bounds(sym: Symbol)(using Context): TypeBounds | Null = - mapping(sym) match { - case null => null - // TODO: Improve flow typing so that ascription becomes redundant - case tv: TypeVar => - def retrieveBounds: TypeBounds = externalize(bounds(tv.origin)).bounds - retrieveBounds - //.showing(i"gadt bounds $sym: $result", gadts) - //.ensuring(containsNoInternalTypes(_)) - } - - /** Is the symbol registered in the constraint? - * - * @note this is true even if the symbol is constrained to be equal to another type, unlike [[Constraint.contains]]. - */ - def contains(sym: Symbol)(using Context): Boolean = mapping(sym) != null - - /** GADT constraint narrows bounds of at least one variable */ - def isNarrowing: Boolean = wasConstrained + def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = gadt.isLess(sym1, sym2) + def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.fullBounds(sym) + def bounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.bounds(sym) + def contains(sym: Symbol)(using Context): Boolean = gadt.contains(sym) + def isNarrowing: Boolean = gadt.isNarrowing + def symbols: List[Symbol] = gadt.symbols /** See [[ConstraintHandling.approximation]] */ def approximation(sym: Symbol, fromBelow: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type = { - val res = - approximation(tvarOrError(sym).origin, fromBelow, maxLevel) match - case tpr: TypeParamRef => - // Here we do externalization when the returned type is a TypeParamRef, - // b/c ConstraintHandling.approximation may return internal types when - // the type variable is instantiated. See #15531. - externalize(tpr) - case tp => tp - - gadts.println(i"approximating $sym ~> $res") - res + approximation(gadt.tvarOrError(sym).origin, fromBelow, maxLevel).match + case tpr: TypeParamRef => + // Here we do externalization when the returned type is a TypeParamRef, + // b/c ConstraintHandling.approximation may return internal types when + // the type variable is instantiated. See #15531. + gadt.externalize(tpr) + case tp => tp + .showing(i"approximating $sym ~> $result", gadts) } - def symbols: List[Symbol] = mapping.keys + def fresh: GadtConstraintHandling = GadtConstraintHandling(gadt) - def fresh: GadtConstraint = new ProperGadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) - - /** Restore the state from other [[GadtConstraint]], probably copied using [[fresh]] */ - def restore(other: GadtConstraint): Unit = - this.myConstraint = other.myConstraint - this.mapping = other.mapping - this.reverseMapping = other.reverseMapping - this.wasConstrained = other.wasConstrained - - def restore(constr: Constraint, mapping: SimpleIdentityMap[Symbol, TypeVar], revMapping: SimpleIdentityMap[TypeParamRef, Symbol], wasConstrained: Boolean): Unit = - this.myConstraint = constr - this.mapping = mapping - this.reverseMapping = revMapping - this.wasConstrained = wasConstrained + /** Restore the GadtConstraint state. */ + def restore(gadt: GadtConstraint): Unit = this.gadt = gadt inline def rollbackGadtUnless(inline op: Boolean): Boolean = - val savedConstr = myConstraint - val savedMapping = mapping - val savedReverseMapping = reverseMapping - val savedWasConstrained = wasConstrained + val saved = gadt var result = false - try - result = op - finally - if !result then - restore(savedConstr, savedMapping, savedReverseMapping, savedWasConstrained) + try result = op + finally if !result then restore(saved) result // ---- Protected/internal ----------------------------------------------- - override protected def constraint = myConstraint - override protected def constraint_=(c: Constraint) = myConstraint = c + override protected def constraint = gadt.constraint + override protected def constraint_=(c: Constraint) = gadt = gadt.withConstraint(c) override protected def isSub(tp1: Type, tp2: Type)(using Context): Boolean = TypeComparer.isSubType(tp1, tp2) override protected def isSame(tp1: Type, tp2: Type)(using Context): Boolean = TypeComparer.isSameType(tp1, tp2) - override def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = - externalize(constraint.nonParamBounds(param)).bounds - - override def fullLowerBound(param: TypeParamRef)(using Context): Type = - constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { - (t, u) => t | externalize(u) - } - - override def fullUpperBound(param: TypeParamRef)(using Context): Type = - constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (t, u) => - val eu = externalize(u) - // Any as the upper bound means "no bound", but if F is higher-kinded, - // Any & F = F[_]; this is wrong for us so we need to short-circuit - if t.isAny then eu else t & eu - } - - // ---- Private ---------------------------------------------------------- - - private def externalize(tp: Type, theMap: TypeMap | Null = null)(using Context): Type = tp match - case param: TypeParamRef => reverseMapping(param) match - case sym: Symbol => sym.typeRef - case null => param - case tp: TypeAlias => tp.derivedAlias(externalize(tp.alias, theMap)) - case tp => (if theMap == null then ExternalizeMap() else theMap).mapOver(tp) - - private class ExternalizeMap(using Context) extends TypeMap: - def apply(tp: Type): Type = externalize(tp, this)(using mapCtx) - - private def tvarOrError(sym: Symbol)(using Context): TypeVar = - mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN - - private def containsNoInternalTypes(tp: Type, theAcc: TypeAccumulator[Boolean] | Null = null)(using Context): Boolean = tp match { - case tpr: TypeParamRef => !reverseMapping.contains(tpr) - case tv: TypeVar => !reverseMapping.contains(tv.origin) - case tp => - (if (theAcc != null) theAcc else new ContainsNoInternalTypesAccumulator()).foldOver(true, tp) - } - - private class ContainsNoInternalTypesAccumulator(using Context) extends TypeAccumulator[Boolean] { - override def apply(x: Boolean, tp: Type): Boolean = x && containsNoInternalTypes(tp, this) - } + override def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = gadt.nonParamBounds(param) + override def fullLowerBound(param: TypeParamRef)(using Context): Type = gadt.fullLowerBound(param) + override def fullUpperBound(param: TypeParamRef)(using Context): Type = gadt.fullUpperBound(param) // ---- Debug ------------------------------------------------------------ override def constr = gadtsConstr - - override def toText(printer: Printer): Texts.Text = printer.toText(this) - - /** Provides more information than toText, by showing the underlying Constraint details. */ - def debugBoundsDescription(using Context): String = i"$this\n$constraint" } -private class ProperGadtConstraint ( - myConstraint: Constraint, - mapping: SimpleIdentityMap[Symbol, TypeVar], - reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol], - wasConstrained: Boolean, -) extends ConstraintHandling with GadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) +// Hide ConstraintHandling within GadtConstraintHandling +private class ProperGadtConstraintHandling(gadt: GadtConstraint) extends ConstraintHandling with GadtConstraintHandling(gadt) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 212b70336f4b..faea30390d2b 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -224,6 +224,17 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] = upper(param).filterNot(isLess(butNot, _)) + def bounds(param: TypeParamRef)(using Context): TypeBounds = { + val e = entry(param) + if (e.exists) e.bounds + else { + // TODO: should we change the type of paramInfos to nullable? + val pinfos: List[param.binder.PInfo] | Null = param.binder.paramInfos + if (pinfos != null) pinfos(param.paramNum) // pinfos == null happens in pos/i536.scala + else TypeBounds.empty + } + } + // ---------- Info related to TypeParamRefs ------------------------------------------- def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index e7f54d088c09..9fff257ee963 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -261,7 +261,7 @@ trait PatternTypeConstrainer { self: TypeComparer => val assumeInvariantRefinement = migrateTo3 || forceInvariantRefinement || refinementIsInvariant(patternTp) - trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt}") { + trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt.gadt}") { (tp, pt) match { case (AppliedType(tyconS, argsS), AppliedType(tyconP, argsP)) => val saved = state.nn.constraint diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index a0eb5139eb07..0bb1529ef396 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1442,14 +1442,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if tp2 eq NoType then false else if tp1 eq tp2 then true else - val saved = constraint - val savedGadtConstr = ctx.gadt.getConstraint - val savedMapping = ctx.gadt.getMapping - val savedReverseMapping = ctx.gadt.getReverseMapping - val savedWasConstrained = ctx.gadt.getWasConstrained + val savedCstr = constraint + val savedGadt = ctx.gadt.gadt inline def restore() = - state.constraint = saved - ctx.gadt.restore(savedGadtConstr, savedMapping, savedReverseMapping, savedWasConstrained) + state.constraint = savedCstr + ctx.gadt.restore(savedGadt) val savedSuccessCount = successCount try recCount += 1 @@ -1855,22 +1852,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling */ private def necessaryEither(op1: => Boolean, op2: => Boolean): Boolean = val preConstraint = constraint - val preGadt = ctx.gadt.fresh + val preGadtHandling = ctx.gadt.fresh + val preGadt = preGadtHandling.gadt def allSubsumes(leftGadt: GadtConstraint, rightGadt: GadtConstraint, left: Constraint, right: Constraint): Boolean = - subsumes(left, right, preConstraint) && preGadt.subsumes(leftGadt, rightGadt, preGadt) + subsumes(left, right, preConstraint) && preGadtHandling.subsumes(leftGadt, rightGadt, preGadt) if op1 then val op1Constraint = constraint - val op1Gadt = ctx.gadt.fresh + val op1Gadt = ctx.gadt.gadt constraint = preConstraint ctx.gadt.restore(preGadt) if op2 then - if allSubsumes(op1Gadt, ctx.gadt, op1Constraint, constraint) then - gadts.println(i"GADT CUT - prefer ${ctx.gadt} over $op1Gadt") + if allSubsumes(op1Gadt, ctx.gadt.gadt, op1Constraint, constraint) then + gadts.println(i"GADT CUT - prefer ${ctx.gadt.gadt} over $op1Gadt") constr.println(i"CUT - prefer $constraint over $op1Constraint") - else if allSubsumes(ctx.gadt, op1Gadt, constraint, op1Constraint) then - gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt}") + else if allSubsumes(ctx.gadt.gadt, op1Gadt, constraint, op1Constraint) then + gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt.gadt}") constr.println(i"CUT - prefer $op1Constraint over $constraint") constraint = op1Constraint ctx.gadt.restore(op1Gadt) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5abb32b15d57..19a18305ee65 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -269,7 +269,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case CaseDef(pat, _, _) => val gadtCtx = pat.removeAttachment(typer.Typer.InferredGadtConstraints) match - case Some(gadt) => ctx.fresh.setGadt(gadt) + case Some(gadt) => ctx.fresh.setGadt(GadtConstraintHandling(gadt)) case None => ctx super.transform(tree)(using gadtCtx) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3e0e7dd5879d..596ad01bb888 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1030,7 +1030,7 @@ trait Implicits: case result: SearchSuccess => if result.tstate ne ctx.typerState then result.tstate.commit() - if result.gstate ne ctx.gadt then + if result.gstate ne ctx.gadt.gadt then ctx.gadt.restore(result.gstate) if hasSkolem(false, result.tree) then report.error(SkolemInInferred(result.tree, pt, argument), ctx.source.atSpan(span)) @@ -1145,7 +1145,7 @@ trait Implicits: SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } else - SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) + SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt.gadt) } /** An implicit search; parameters as in `inferImplicit` */ @@ -1343,7 +1343,7 @@ trait Implicits: case _: SearchFailure => SearchSuccess(ref(defn.NotGiven_value), defn.NotGiven_value.termRef, 0)( ctx.typerState.fresh().setCommittable(true), - ctx.gadt + ctx.gadt.gadt ) case _: SearchSuccess => NoMatchingImplicitsFailure @@ -1526,7 +1526,7 @@ trait Implicits: // other candidates need to be considered. recursiveRef match case ref: TermRef => - SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt) + SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt.gadt) case _ => searchImplicit(contextual = true) end bestImplicit diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1a24a94e527e..1cb723b85c27 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1782,7 +1782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // see tests/pos/i12226 and issue #12226. It might be possible that this // will end up taking too much memory. If it does, we should just limit // how much GADT constraints we infer - it's always sound to infer less. - pat1.putAttachment(InferredGadtConstraints, ctx.gadt) + pat1.putAttachment(InferredGadtConstraints, ctx.gadt.gadt) if (pt1.isValueType) // insert a cast if body does not conform to expected type if we disregard gadt bounds body1 = body1.ensureConforms(pt1)(using originalCtx) assignType(cpy.CaseDef(tree)(pat1, guard1, body1), pat1, body1) @@ -3835,7 +3835,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer adaptToSubType(wtp) case CompareResult.OKwithGADTUsed if pt.isValueType - && !inContext(ctx.fresh.setGadt(GadtConstraint.empty)) { + && !inContext(ctx.fresh.setGadt(GadtConstraintHandling(GadtConstraint.empty))) { val res = (tree.tpe.widenExpr frozen_<:< pt) if res then // we overshot; a cast is not needed, after all. From 58d0a291e64cb177357872ad4927e712b5df18a5 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 24 Jan 2023 12:16:05 +0000 Subject: [PATCH 07/97] Rename GadtConstraintHandling to GadtState --- .../src/dotty/tools/dotc/core/Contexts.scala | 21 ++++++------ .../tools/dotc/core/GadtConstraint.scala | 33 +++++++------------ .../src/dotty/tools/dotc/core/NamerOps.scala | 6 ++-- .../dotc/core/PatternTypeConstrainer.scala | 4 +-- .../src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../dotty/tools/dotc/core/TypeComparer.scala | 32 +++++++++--------- .../src/dotty/tools/dotc/core/TypeOps.scala | 6 ++-- .../tools/dotc/inlines/InlineReducer.scala | 4 +-- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 10 +++--- .../dotty/tools/dotc/typer/Inferencing.scala | 4 +-- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 8 ++--- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 14 files changed, 63 insertions(+), 73 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 398af92e494d..2f28975dd066 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -141,7 +141,8 @@ object Contexts { def tree: Tree[?] def scope: Scope def typerState: TyperState - def gadt: GadtConstraintHandling + def gadt: GadtConstraint = gadtState.gadt + def gadtState: GadtState def searchHistory: SearchHistory def source: SourceFile @@ -410,7 +411,7 @@ object Contexts { val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next() superOrThisCallContext(owner, constrCtx.scope) .setTyperState(typerState) - .setGadt(gadt) + .setGadtState(gadtState) .fresh .setScope(this.scope) } @@ -541,8 +542,8 @@ object Contexts { private var _typerState: TyperState = uninitialized final def typerState: TyperState = _typerState - private var _gadt: GadtConstraintHandling = uninitialized - final def gadt: GadtConstraintHandling = _gadt + private var _gadtState: GadtState = uninitialized + final def gadtState: GadtState = _gadtState private var _searchHistory: SearchHistory = uninitialized final def searchHistory: SearchHistory = _searchHistory @@ -567,7 +568,7 @@ object Contexts { _owner = origin.owner _tree = origin.tree _scope = origin.scope - _gadt = origin.gadt + _gadtState = origin.gadtState _searchHistory = origin.searchHistory _source = origin.source _moreProperties = origin.moreProperties @@ -624,12 +625,12 @@ object Contexts { this._scope = typer.scope setTypeAssigner(typer) - def setGadt(gadt: GadtConstraintHandling): this.type = - util.Stats.record("Context.setGadt") - this._gadt = gadt + def setGadtState(gadtState: GadtState): this.type = + util.Stats.record("Context.setGadtState") + this._gadtState = gadtState this def setFreshGADTBounds: this.type = - setGadt(gadt.fresh) + setGadtState(gadtState.fresh) def setSearchHistory(searchHistory: SearchHistory): this.type = util.Stats.record("Context.setSearchHistory") @@ -721,7 +722,7 @@ object Contexts { .updated(notNullInfosLoc, Nil) .updated(compilationUnitLoc, NoCompilationUnit) c._searchHistory = new SearchRoot - c._gadt = GadtConstraintHandling(GadtConstraint.empty) + c._gadtState = GadtState(GadtConstraint.empty) c end FreshContext diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index d683c15a9241..a863a982a44d 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -133,20 +133,14 @@ class GadtConstraint private ( ): GadtConstraint = GadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) end GadtConstraint -object GadtConstraintHandling: - def apply(gadt: GadtConstraint): GadtConstraintHandling = new ProperGadtConstraintHandling(gadt) +object GadtState: + def apply(gadt: GadtConstraint): GadtState = ProperGadtState(gadt) -sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { - this: ConstraintHandling => +sealed trait GadtState { + this: ConstraintHandling => // Hide ConstraintHandling within GadtConstraintHandling - def gadt: GadtConstraint = myGadt - private def gadt_=(g: GadtConstraint) = myGadt = g - - /** Exposes ConstraintHandling.subsumes */ - def subsumes(left: GadtConstraint, right: GadtConstraint, pre: GadtConstraint)(using Context): Boolean = { - def extractConstraint(g: GadtConstraint) = g.constraint - subsumes(extractConstraint(left), extractConstraint(right), extractConstraint(pre)) - } + def gadt: GadtConstraint + def gadt_=(g: GadtConstraint): Unit override protected def legalBound(param: TypeParamRef, rawBound: Type, isUpper: Boolean)(using Context): Type = // GADT constraints never involve wildcards and are not propagated outside @@ -233,13 +227,6 @@ sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { result } - def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = gadt.isLess(sym1, sym2) - def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.fullBounds(sym) - def bounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.bounds(sym) - def contains(sym: Symbol)(using Context): Boolean = gadt.contains(sym) - def isNarrowing: Boolean = gadt.isNarrowing - def symbols: List[Symbol] = gadt.symbols - /** See [[ConstraintHandling.approximation]] */ def approximation(sym: Symbol, fromBelow: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type = { approximation(gadt.tvarOrError(sym).origin, fromBelow, maxLevel).match @@ -252,7 +239,7 @@ sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { .showing(i"approximating $sym ~> $result", gadts) } - def fresh: GadtConstraintHandling = GadtConstraintHandling(gadt) + def fresh: GadtState = GadtState(gadt) /** Restore the GadtConstraint state. */ def restore(gadt: GadtConstraint): Unit = this.gadt = gadt @@ -281,5 +268,7 @@ sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { override def constr = gadtsConstr } -// Hide ConstraintHandling within GadtConstraintHandling -private class ProperGadtConstraintHandling(gadt: GadtConstraint) extends ConstraintHandling with GadtConstraintHandling(gadt) +// Hide ConstraintHandling within GadtState +private class ProperGadtState(private var myGadt: GadtConstraint) extends ConstraintHandling with GadtState: + def gadt: GadtConstraint = myGadt + def gadt_=(gadt: GadtConstraint): Unit = myGadt = gadt diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 66912537dbce..db6f72590818 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -212,11 +212,11 @@ object NamerOps: * by (ab?)-using GADT constraints. See pos/i941.scala. */ def linkConstructorParams(sym: Symbol, tparams: List[Symbol], rhsCtx: Context)(using Context): Unit = - rhsCtx.gadt.addToConstraint(tparams) + rhsCtx.gadtState.addToConstraint(tparams) tparams.lazyZip(sym.owner.typeParams).foreach { (psym, tparam) => val tr = tparam.typeRef - rhsCtx.gadt.addBound(psym, tr, isUpper = false) - rhsCtx.gadt.addBound(psym, tr, isUpper = true) + rhsCtx.gadtState.addBound(psym, tr, isUpper = false) + rhsCtx.gadtState.addBound(psym, tr, isUpper = true) } end NamerOps diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index 9fff257ee963..5e8a960608e6 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -261,12 +261,12 @@ trait PatternTypeConstrainer { self: TypeComparer => val assumeInvariantRefinement = migrateTo3 || forceInvariantRefinement || refinementIsInvariant(patternTp) - trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt.gadt}") { + trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt}") { (tp, pt) match { case (AppliedType(tyconS, argsS), AppliedType(tyconP, argsP)) => val saved = state.nn.constraint val result = - ctx.gadt.rollbackGadtUnless { + ctx.gadtState.rollbackGadtUnless { tyconS.typeParams.lazyZip(argsS).lazyZip(argsP).forall { (param, argS, argP) => val variance = param.paramVarianceSign if variance == 0 || assumeInvariantRefinement || diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index d14be2b0dfb9..aa3ae0c3c513 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -686,7 +686,7 @@ object Symbols { addToGadt: Boolean = true, flags: FlagSet = EmptyFlags)(using Context): Symbol = { val sym = newSymbol(ctx.owner, name, Case | flags, info, coord = span) - if (addToGadt && name.isTypeName) ctx.gadt.addToConstraint(sym) + if (addToGadt && name.isTypeName) ctx.gadtState.addToConstraint(sym) sym } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 0bb1529ef396..cd1e55ef028c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -116,7 +116,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private def isBottom(tp: Type) = tp.widen.isRef(NothingClass) protected def gadtBounds(sym: Symbol)(using Context) = ctx.gadt.bounds(sym) - protected def gadtAddBound(sym: Symbol, b: Type, isUpper: Boolean): Boolean = ctx.gadt.addBound(sym, b, isUpper) + protected def gadtAddBound(sym: Symbol, b: Type, isUpper: Boolean): Boolean = ctx.gadtState.addBound(sym, b, isUpper) protected def typeVarInstance(tvar: TypeVar)(using Context): Type = tvar.underlying @@ -1443,10 +1443,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling else if tp1 eq tp2 then true else val savedCstr = constraint - val savedGadt = ctx.gadt.gadt + val savedGadt = ctx.gadt inline def restore() = state.constraint = savedCstr - ctx.gadt.restore(savedGadt) + ctx.gadtState.restore(savedGadt) val savedSuccessCount = successCount try recCount += 1 @@ -1852,34 +1852,34 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling */ private def necessaryEither(op1: => Boolean, op2: => Boolean): Boolean = val preConstraint = constraint - val preGadtHandling = ctx.gadt.fresh - val preGadt = preGadtHandling.gadt + val preGadt = ctx.gadt def allSubsumes(leftGadt: GadtConstraint, rightGadt: GadtConstraint, left: Constraint, right: Constraint): Boolean = - subsumes(left, right, preConstraint) && preGadtHandling.subsumes(leftGadt, rightGadt, preGadt) + subsumes(left, right, preConstraint) + && subsumes(leftGadt.constraint, rightGadt.constraint, preGadt.constraint) if op1 then val op1Constraint = constraint - val op1Gadt = ctx.gadt.gadt + val op1Gadt = ctx.gadt constraint = preConstraint - ctx.gadt.restore(preGadt) + ctx.gadtState.restore(preGadt) if op2 then - if allSubsumes(op1Gadt, ctx.gadt.gadt, op1Constraint, constraint) then - gadts.println(i"GADT CUT - prefer ${ctx.gadt.gadt} over $op1Gadt") + if allSubsumes(op1Gadt, ctx.gadt, op1Constraint, constraint) then + gadts.println(i"GADT CUT - prefer ${ctx.gadt} over $op1Gadt") constr.println(i"CUT - prefer $constraint over $op1Constraint") - else if allSubsumes(ctx.gadt.gadt, op1Gadt, constraint, op1Constraint) then - gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt.gadt}") + else if allSubsumes(ctx.gadt, op1Gadt, constraint, op1Constraint) then + gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt}") constr.println(i"CUT - prefer $op1Constraint over $constraint") constraint = op1Constraint - ctx.gadt.restore(op1Gadt) + ctx.gadtState.restore(op1Gadt) else gadts.println(i"GADT CUT - no constraint is preferable, reverting to $preGadt") constr.println(i"CUT - no constraint is preferable, reverting to $preConstraint") constraint = preConstraint - ctx.gadt.restore(preGadt) + ctx.gadtState.restore(preGadt) else constraint = op1Constraint - ctx.gadt.restore(op1Gadt) + ctx.gadtState.restore(op1Gadt) true else op2 end necessaryEither @@ -2051,7 +2051,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling gadts.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.toString} ${bound.isRef(tparam)}") if (bound.isRef(tparam)) false else - ctx.gadt.rollbackGadtUnless(gadtAddBound(tparam, bound, isUpper)) + ctx.gadtState.rollbackGadtUnless(gadtAddBound(tparam, bound, isUpper)) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index ea8dcee5fca5..d9da11c561e8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -687,8 +687,8 @@ object TypeOps: val bound1 = massage(bound) if (bound1 ne bound) { if (checkCtx eq ctx) checkCtx = ctx.fresh.setFreshGADTBounds - if (!checkCtx.gadt.contains(sym)) checkCtx.gadt.addToConstraint(sym) - checkCtx.gadt.addBound(sym, bound1, fromBelow) + if (!checkCtx.gadt.contains(sym)) checkCtx.gadtState.addToConstraint(sym) + checkCtx.gadtState.addBound(sym, bound1, fromBelow) typr.println("install GADT bound $bound1 for when checking F-bounded $sym") } } @@ -872,7 +872,7 @@ object TypeOps: case tp: TypeRef if tp.symbol.exists && !tp.symbol.isClass => foldOver(tp.symbol :: xs, tp) case tp => foldOver(xs, tp) val syms2 = getAbstractSymbols(Nil, tp2).reverse - if syms2.nonEmpty then ctx.gadt.addToConstraint(syms2) + if syms2.nonEmpty then ctx.gadtState.addToConstraint(syms2) // If parent contains a reference to an abstract type, then we should // refine subtype checking to eliminate abstract types according to diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 42e86b71eff8..e1b2aaa02866 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -311,11 +311,11 @@ class InlineReducer(inliner: Inliner)(using Context): def addTypeBindings(typeBinds: TypeBindsMap)(using Context): Unit = typeBinds.foreachBinding { case (sym, shouldBeMinimized) => newTypeBinding(sym, - ctx.gadt.approximation(sym, fromBelow = shouldBeMinimized, maxLevel = Int.MaxValue)) + ctx.gadtState.approximation(sym, fromBelow = shouldBeMinimized, maxLevel = Int.MaxValue)) } def registerAsGadtSyms(typeBinds: TypeBindsMap)(using Context): Unit = - if (typeBinds.size > 0) ctx.gadt.addToConstraint(typeBinds.keys) + if (typeBinds.size > 0) ctx.gadtState.addToConstraint(typeBinds.keys) pat match { case Typed(pat1, tpt) => diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 19a18305ee65..2039a8f19558 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -269,7 +269,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case CaseDef(pat, _, _) => val gadtCtx = pat.removeAttachment(typer.Typer.InferredGadtConstraints) match - case Some(gadt) => ctx.fresh.setGadt(GadtConstraintHandling(gadt)) + case Some(gadt) => ctx.fresh.setGadtState(GadtState(gadt)) case None => ctx super.transform(tree)(using gadtCtx) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 596ad01bb888..03d3011b4bcd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1030,8 +1030,8 @@ trait Implicits: case result: SearchSuccess => if result.tstate ne ctx.typerState then result.tstate.commit() - if result.gstate ne ctx.gadt.gadt then - ctx.gadt.restore(result.gstate) + if result.gstate ne ctx.gadt then + ctx.gadtState.restore(result.gstate) if hasSkolem(false, result.tree) then report.error(SkolemInInferred(result.tree, pt, argument), ctx.source.atSpan(span)) implicits.println(i"success: $result") @@ -1145,7 +1145,7 @@ trait Implicits: SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } else - SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt.gadt) + SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) } /** An implicit search; parameters as in `inferImplicit` */ @@ -1343,7 +1343,7 @@ trait Implicits: case _: SearchFailure => SearchSuccess(ref(defn.NotGiven_value), defn.NotGiven_value.termRef, 0)( ctx.typerState.fresh().setCommittable(true), - ctx.gadt.gadt + ctx.gadt ) case _: SearchSuccess => NoMatchingImplicitsFailure @@ -1526,7 +1526,7 @@ trait Implicits: // other candidates need to be considered. recursiveRef match case ref: TermRef => - SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt.gadt) + SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt) case _ => searchImplicit(contextual = true) end bestImplicit diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 2aef3433228b..3442207653d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -262,7 +262,7 @@ object Inferencing { && ctx.gadt.contains(tp.symbol) => val sym = tp.symbol - val res = ctx.gadt.approximation(sym, fromBelow = variance < 0) + val res = ctx.gadtState.approximation(sym, fromBelow = variance < 0) gadts.println(i"approximated $tp ~~ $res") res @@ -432,7 +432,7 @@ object Inferencing { } // We add the created symbols to GADT constraint here. - if (res.nonEmpty) ctx.gadt.addToConstraint(res) + if (res.nonEmpty) ctx.gadtState.addToConstraint(res) res } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6cdd0150518b..6f85efb0fc8a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1864,7 +1864,7 @@ class Namer { typer: Typer => // so we must allow constraining its type parameters // compare with typedDefDef, see tests/pos/gadt-inference.scala rhsCtx.setFreshGADTBounds - rhsCtx.gadt.addToConstraint(typeParams) + rhsCtx.gadtState.addToConstraint(typeParams) } def typedAheadRhs(pt: Type) = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1cb723b85c27..eb09d30e60f3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1782,7 +1782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // see tests/pos/i12226 and issue #12226. It might be possible that this // will end up taking too much memory. If it does, we should just limit // how much GADT constraints we infer - it's always sound to infer less. - pat1.putAttachment(InferredGadtConstraints, ctx.gadt.gadt) + pat1.putAttachment(InferredGadtConstraints, ctx.gadt) if (pt1.isValueType) // insert a cast if body does not conform to expected type if we disregard gadt bounds body1 = body1.ensureConforms(pt1)(using originalCtx) assignType(cpy.CaseDef(tree)(pat1, guard1, body1), pat1, body1) @@ -2362,7 +2362,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ctx.outer.outersIterator.takeWhile(!_.owner.is(Method)) .filter(ctx => ctx.owner.isClass && ctx.owner.typeParams.nonEmpty) .toList.reverse - .foreach(ctx => rhsCtx.gadt.addToConstraint(ctx.owner.typeParams)) + .foreach(ctx => rhsCtx.gadtState.addToConstraint(ctx.owner.typeParams)) if tparamss.nonEmpty then rhsCtx.setFreshGADTBounds @@ -2371,7 +2371,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // we're typing a polymorphic definition's body, // so we allow constraining all of its type parameters // constructors are an exception as we don't allow constraining type params of classes - rhsCtx.gadt.addToConstraint(tparamSyms) + rhsCtx.gadtState.addToConstraint(tparamSyms) else if !sym.isPrimaryConstructor then linkConstructorParams(sym, tparamSyms, rhsCtx) @@ -3835,7 +3835,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer adaptToSubType(wtp) case CompareResult.OKwithGADTUsed if pt.isValueType - && !inContext(ctx.fresh.setGadt(GadtConstraintHandling(GadtConstraint.empty))) { + && !inContext(ctx.fresh.setGadtState(GadtState(GadtConstraint.empty))) { val res = (tree.tpe.widenExpr frozen_<:< pt) if res then // we overshot; a cast is not needed, after all. diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index dd6471a882bd..4d08e0582d1d 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3130,7 +3130,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler if typeHoles.isEmpty then ctx else val ctx1 = ctx.fresh.setFreshGADTBounds.addMode(dotc.core.Mode.GadtConstraintInference) - ctx1.gadt.addToConstraint(typeHoles) + ctx1.gadtState.addToConstraint(typeHoles) ctx1 val matchings = QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1) From abbb54956c96013b9fb512a008481e819e51a4c2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 13 Feb 2023 22:23:12 +0000 Subject: [PATCH 08/97] Avoid bidirectional GADT typebounds from fullBounds --- .../tools/dotc/core/GadtConstraint.scala | 22 +++++++- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/pos/i14287.min.scala | 7 +++ tests/pos/i14287.scala | 16 ++++++ tests/pos/i15523.avoid.scala | 8 +++ tests/pos/i15523.scala | 7 +++ tests/pos/i16777.scala | 52 +++++++++++++++++++ 7 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i14287.min.scala create mode 100644 tests/pos/i14287.scala create mode 100644 tests/pos/i15523.avoid.scala create mode 100644 tests/pos/i15523.scala create mode 100644 tests/pos/i16777.scala diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index a863a982a44d..211c8e637b1f 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -71,12 +71,26 @@ class GadtConstraint private ( externalize(constraint.nonParamBounds(param)).bounds def fullLowerBound(param: TypeParamRef)(using Context): Type = - constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { + val self = externalize(param) + constraint.minLower(param).filterNot { p => + val sym = paramSymbol(p) + sym.name.is(NameKinds.UniqueName) && { + val hi = sym.info.hiBound + !hi.isExactlyAny && self <:< hi + } + }.foldLeft(nonParamBounds(param).lo) { (t, u) => t | externalize(u) } def fullUpperBound(param: TypeParamRef)(using Context): Type = - constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (t, u) => + val self = externalize(param) + constraint.minUpper(param).filterNot { p => + val sym = paramSymbol(p) + sym.name.is(NameKinds.UniqueName) && { + val lo = sym.info.loBound + !lo.isExactlyNothing && lo <:< self + } + }.foldLeft(nonParamBounds(param).hi) { (t, u) => val eu = externalize(u) // Any as the upper bound means "no bound", but if F is higher-kinded, // Any & F = F[_]; this is wrong for us so we need to short-circuit @@ -96,6 +110,10 @@ class GadtConstraint private ( def tvarOrError(sym: Symbol)(using Context): TypeVar = mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN + private def paramSymbol(p: TypeParamRef): Symbol = reverseMapping(p) match + case sym: Symbol => sym + case null => NoSymbol + @tailrec final def stripInternalTypeVar(tp: Type): Type = tp match case tv: TypeVar => val inst = constraint.instType(tv) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index eb09d30e60f3..cb2758c088b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1743,7 +1743,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else report.error(new DuplicateBind(b, cdef), b.srcPos) if (!ctx.isAfterTyper) { val bounds = ctx.gadt.fullBounds(sym) - if (bounds != null) sym.info = bounds + if (bounds != null) sym.info = checkNonCyclic(sym, bounds, reportErrors = true) } b case t: UnApply if t.symbol.is(Inline) => Inlines.inlinedUnapply(t) diff --git a/tests/pos/i14287.min.scala b/tests/pos/i14287.min.scala new file mode 100644 index 000000000000..8f7773ab0ac8 --- /dev/null +++ b/tests/pos/i14287.min.scala @@ -0,0 +1,7 @@ +enum Foo[+F[_]]: + case Bar[B[_]](value: Foo[B]) extends Foo[B] + +class Test: + def test[X[_]](foo: Foo[X]): Foo[X] = foo match + case Foo.Bar(Foo.Bar(x)) => Foo.Bar(x) + case _ => foo diff --git a/tests/pos/i14287.scala b/tests/pos/i14287.scala new file mode 100644 index 000000000000..1291dc8adefc --- /dev/null +++ b/tests/pos/i14287.scala @@ -0,0 +1,16 @@ +// scalac: -Yno-deep-subtypes +enum Free[+F[_], A]: + case Return(a: A) + case Suspend(s: F[A]) + case FlatMap[F[_], A, B]( + s: Free[F, A], + f: A => Free[F, B]) extends Free[F, B] + + def flatMap[F2[x] >: F[x], B](f: A => Free[F2,B]): Free[F2,B] = + FlatMap(this, f) + + @scala.annotation.tailrec + final def step: Free[F, A] = this match + case FlatMap(FlatMap(fx, f), g) => fx.flatMap(x => f(x).flatMap(y => g(y))).step + case FlatMap(Return(x), f) => f(x).step + case _ => this diff --git a/tests/pos/i15523.avoid.scala b/tests/pos/i15523.avoid.scala new file mode 100644 index 000000000000..afbfc1a69d60 --- /dev/null +++ b/tests/pos/i15523.avoid.scala @@ -0,0 +1,8 @@ +// scalac: -Werror +// like the original, but with a case body `a` +// which caused type avoidance to infinitely recurse +sealed trait Parent +final case class Leaf[A, B >: A](a: A, b: B) extends Parent + +def run(x: Parent) = x match + case Leaf(a, _) => a diff --git a/tests/pos/i15523.scala b/tests/pos/i15523.scala new file mode 100644 index 000000000000..cf63613c29ac --- /dev/null +++ b/tests/pos/i15523.scala @@ -0,0 +1,7 @@ +// scalac: -Werror +sealed trait Parent +final case class Leaf[A, B >: A](a: A, b: B) extends Parent + +def run(x: Parent): Unit = x match { + case Leaf(a, b) => +} diff --git a/tests/pos/i16777.scala b/tests/pos/i16777.scala new file mode 100644 index 000000000000..4218aea29d9f --- /dev/null +++ b/tests/pos/i16777.scala @@ -0,0 +1,52 @@ +// scalac: -Ykind-projector:underscores + +sealed abstract class Free[+S[_, _], +E, +A] { + @inline final def flatMap[S1[e, a] >: S[e, a], B, E1 >: E](fun: A => Free[S1, E1, B]): Free[S1, E1, B] = Free.FlatMapped[S1, E, E1, A, B](this, fun) + @inline final def map[B](fun: A => B): Free[S, E, B] = flatMap(a => Free.pure[S, B](fun(a))) + @inline final def as[B](as: => B): Free[S, E, B] = map(_ => as) + @inline final def *>[S1[e, a] >: S[e, a], B, E1 >: E](sc: Free[S1, E1, B]): Free[S1, E1, B] = flatMap(_ => sc) + @inline final def <*[S1[e, a] >: S[e, a], B, E1 >: E](sc: Free[S1, E1, B]): Free[S1, E1, A] = flatMap(r => sc.as(r)) + + @inline final def void: Free[S, E, Unit] = map(_ => ()) + + // FIXME: Scala 3.1.4 bug: false unexhaustive match warning + /// @nowarn("msg=pattern case: Free.FlatMapped") + @inline final def foldMap[S1[e, a] >: S[e, a], G[+_, +_]](transform: S1 ~>> G)(implicit G: Monad2[G]): G[E, A] = { + this match { + case Free.Pure(a) => G.pure(a) + case Free.Suspend(a) => transform.apply(a) + case Free.FlatMapped(sub, cont) => + sub match { + case Free.FlatMapped(sub2, cont2) => sub2.flatMap(a => cont2(a).flatMap(cont)).foldMap(transform) + case another => G.flatMap(another.foldMap(transform))(cont(_).foldMap(transform)) + } + } + } +} + +trait ~>>[-F[_, _], +G[_, _]] { + def apply[E, A](f: F[E, A]): G[E, A] +} + +object Free { + @inline def pure[S[_, _], A](a: A): Free[S, Nothing, A] = Pure(a) + @inline def lift[S[_, _], E, A](s: S[E, A]): Free[S, E, A] = Suspend(s) + + final case class Pure[S[_, _], A](a: A) extends Free[S, Nothing, A] { + override def toString: String = s"Pure:[$a]" + } + final case class Suspend[S[_, _], E, A](a: S[E, A]) extends Free[S, E, A] { + override def toString: String = s"Suspend:[$a]" + } + final case class FlatMapped[S[_, _], E, E1 >: E, A, B](sub: Free[S, E, A], cont: A => Free[S, E1, B]) extends Free[S, E1, B] { + override def toString: String = s"FlatMapped:[sub=$sub]" + } +} + +type Monad2[F[+_, +_]] = Monad3[λ[(`-R`, `+E`, `+A`) => F[E, A]]] + +trait Monad3[F[-_, +_, +_]] { + def flatMap[R, E, A, B](r: F[R, E, A])(f: A => F[R, E, B]): F[R, E, B] + def flatten[R, E, A](r: F[R, E, F[R, E, A]]): F[R, E, A] = flatMap(r)(identity) + def pure[A](a: A): F[Any, Nothing, A] +} From 6d141f38875fd3a71ae57d5b826c461e5a249a10 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 14 Feb 2023 13:26:11 +0000 Subject: [PATCH 09/97] Drop failsafe checkNonCyclic and document GADT fullBounds change --- compiler/src/dotty/tools/dotc/core/GadtConstraint.scala | 9 ++++++++- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 211c8e637b1f..4b580c06d11f 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -77,6 +77,13 @@ class GadtConstraint private ( sym.name.is(NameKinds.UniqueName) && { val hi = sym.info.hiBound !hi.isExactlyAny && self <:< hi + // drop any lower param that is a GADT symbol + // and is upper-bounded by a non-Any super-type of the original parameter + // e.g. in pos/i14287.min + // B$1 had info <: X and fullBounds >: B$2 <: X, and + // B$2 had info <: B$1 and fullBounds <: B$1 + // We can use the info of B$2 to drop the lower-bound of B$1 + // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. } }.foldLeft(nonParamBounds(param).lo) { (t, u) => t | externalize(u) @@ -88,7 +95,7 @@ class GadtConstraint private ( val sym = paramSymbol(p) sym.name.is(NameKinds.UniqueName) && { val lo = sym.info.loBound - !lo.isExactlyNothing && lo <:< self + !lo.isExactlyNothing && lo <:< self // same as fullLowerBounds } }.foldLeft(nonParamBounds(param).hi) { (t, u) => val eu = externalize(u) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb2758c088b6..eb09d30e60f3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1743,7 +1743,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else report.error(new DuplicateBind(b, cdef), b.srcPos) if (!ctx.isAfterTyper) { val bounds = ctx.gadt.fullBounds(sym) - if (bounds != null) sym.info = checkNonCyclic(sym, bounds, reportErrors = true) + if (bounds != null) sym.info = bounds } b case t: UnApply if t.symbol.is(Inline) => Inlines.inlinedUnapply(t) From 4cc0e0d80af513866b0d4a65aa471f9296fc82fb Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 21:39:51 +0000 Subject: [PATCH 10/97] GADT: move dropping GADT symbols into foldLeft --- .../tools/dotc/core/GadtConstraint.scala | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 4b580c06d11f..c98445c61a7e 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -3,6 +3,7 @@ package dotc package core import Contexts.*, Decorators.*, Symbols.*, Types.* +import NameKinds.UniqueName import config.Printers.{gadts, gadtsConstr} import util.{SimpleIdentitySet, SimpleIdentityMap} import printing._ @@ -72,36 +73,30 @@ class GadtConstraint private ( def fullLowerBound(param: TypeParamRef)(using Context): Type = val self = externalize(param) - constraint.minLower(param).filterNot { p => - val sym = paramSymbol(p) - sym.name.is(NameKinds.UniqueName) && { - val hi = sym.info.hiBound - !hi.isExactlyAny && self <:< hi - // drop any lower param that is a GADT symbol - // and is upper-bounded by a non-Any super-type of the original parameter - // e.g. in pos/i14287.min - // B$1 had info <: X and fullBounds >: B$2 <: X, and - // B$2 had info <: B$1 and fullBounds <: B$1 - // We can use the info of B$2 to drop the lower-bound of B$1 - // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. - } - }.foldLeft(nonParamBounds(param).lo) { - (t, u) => t | externalize(u) + constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { (acc, p) => + externalize(p) match + case tp: TypeRef + // drop any lower param that is a GADT symbol + // and is upper-bounded by a non-Any super-type of the original parameter + // e.g. in pos/i14287.min + // B$1 had info <: X and fullBounds >: B$2 <: X, and + // B$2 had info <: B$1 and fullBounds <: B$1 + // We can use the info of B$2 to drop the lower-bound of B$1 + // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. + if tp.name.is(UniqueName) && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc + case tp => acc | tp } def fullUpperBound(param: TypeParamRef)(using Context): Type = val self = externalize(param) - constraint.minUpper(param).filterNot { p => - val sym = paramSymbol(p) - sym.name.is(NameKinds.UniqueName) && { - val lo = sym.info.loBound - !lo.isExactlyNothing && lo <:< self // same as fullLowerBounds - } - }.foldLeft(nonParamBounds(param).hi) { (t, u) => - val eu = externalize(u) - // Any as the upper bound means "no bound", but if F is higher-kinded, - // Any & F = F[_]; this is wrong for us so we need to short-circuit - if t.isAny then eu else t & eu + constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (acc, u) => + externalize(u) match + case tp: TypeRef // same as fullLowerBounds + if tp.name.is(UniqueName) && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc + case tp => + // Any as the upper bound means "no bound", but if F is higher-kinded, + // Any & F = F[_]; this is wrong for us so we need to short-circuit + if acc.isAny then tp else acc & tp } def externalize(tp: Type, theMap: TypeMap | Null = null)(using Context): Type = tp match @@ -117,10 +112,6 @@ class GadtConstraint private ( def tvarOrError(sym: Symbol)(using Context): TypeVar = mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN - private def paramSymbol(p: TypeParamRef): Symbol = reverseMapping(p) match - case sym: Symbol => sym - case null => NoSymbol - @tailrec final def stripInternalTypeVar(tp: Type): Type = tp match case tv: TypeVar => val inst = constraint.instType(tv) From 5453d5c5de4e2b3588330614ec5c872332c89564 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 22:25:14 +0000 Subject: [PATCH 11/97] GADT: Use isPatternBound, ofc... --- compiler/src/dotty/tools/dotc/core/GadtConstraint.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index c98445c61a7e..74d668e7ca87 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -83,7 +83,7 @@ class GadtConstraint private ( // B$2 had info <: B$1 and fullBounds <: B$1 // We can use the info of B$2 to drop the lower-bound of B$1 // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. - if tp.name.is(UniqueName) && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc + if tp.symbol.isPatternBound && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc case tp => acc | tp } @@ -92,7 +92,7 @@ class GadtConstraint private ( constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (acc, u) => externalize(u) match case tp: TypeRef // same as fullLowerBounds - if tp.name.is(UniqueName) && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc + if tp.symbol.isPatternBound && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc case tp => // Any as the upper bound means "no bound", but if F is higher-kinded, // Any & F = F[_]; this is wrong for us so we need to short-circuit From ace96f7628d318192bd8840d4ce87da3dba5266d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 22:30:09 +0000 Subject: [PATCH 12/97] GADT: Use =:= instead of Any/Nothing --- .../tools/dotc/core/GadtConstraint.scala | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 74d668e7ca87..bb65cce84042 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -75,15 +75,14 @@ class GadtConstraint private ( val self = externalize(param) constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { (acc, p) => externalize(p) match - case tp: TypeRef - // drop any lower param that is a GADT symbol - // and is upper-bounded by a non-Any super-type of the original parameter - // e.g. in pos/i14287.min - // B$1 had info <: X and fullBounds >: B$2 <: X, and - // B$2 had info <: B$1 and fullBounds <: B$1 - // We can use the info of B$2 to drop the lower-bound of B$1 - // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. - if tp.symbol.isPatternBound && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc + // drop any lower param that is a GADT symbol + // and is upper-bounded by a non-Any super-type of the original parameter + // e.g. in pos/i14287.min + // B$1 had info <: X and fullBounds >: B$2 <: X, and + // B$2 had info <: B$1 and fullBounds <: B$1 + // We can use the info of B$2 to drop the lower-bound of B$1 + // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. + case tp: TypeRef if tp.symbol.isPatternBound && self =:= tp.info.hiBound => acc case tp => acc | tp } @@ -91,8 +90,7 @@ class GadtConstraint private ( val self = externalize(param) constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (acc, u) => externalize(u) match - case tp: TypeRef // same as fullLowerBounds - if tp.symbol.isPatternBound && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc + case tp: TypeRef if tp.symbol.isPatternBound && self =:= tp.info.loBound => acc // like fullLowerBound case tp => // Any as the upper bound means "no bound", but if F is higher-kinded, // Any & F = F[_]; this is wrong for us so we need to short-circuit From fad1584d1b4c518c5637eca516b4f67c9461a642 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 1 Feb 2023 17:39:54 +0100 Subject: [PATCH 13/97] Fix static lazy field holder for GraalVM --- .../lazyvals/InitializedAccessInt.scala | 30 +++++++++++++++++++ .../lazyvals/InitializedObject.scala | 22 ++++++++++++++ .../tools/benchmarks/lazyvals/LazyVals.scala | 18 +++++++++++ .../tools/backend/jvm/BCodeSkelBuilder.scala | 2 +- .../backend/jvm/DottyBackendInterface.scala | 7 +++-- .../dotty/tools/dotc/transform/LazyVals.scala | 11 ++----- 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala new file mode 100644 index 000000000000..2a115ad63496 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala @@ -0,0 +1,30 @@ +package dotty.tools.benchmarks.lazyvals + +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.Blackhole +import LazyVals.LazyIntHolder +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class InitializedAccessInt { + + var holder: LazyIntHolder = _ + + @Setup + def prepare: Unit = { + holder = new LazyIntHolder + holder.value + } + + @Benchmark + def measureInitialized(bh: Blackhole) = { + bh.consume(holder) + bh.consume(holder.value) + } +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala new file mode 100644 index 000000000000..672cc4bf6544 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala @@ -0,0 +1,22 @@ +package dotty.tools.benchmarks.lazyvals + +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.Blackhole +import LazyVals.ObjectHolder +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class InitializedObject { + + @Benchmark + def measureInitialized(bh: Blackhole) = { + bh.consume(ObjectHolder) + bh.consume(ObjectHolder.value) + } +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala index 0afd93d086be..68379f9e142c 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala @@ -50,4 +50,22 @@ object LazyVals { } } } + + class LazyIntHolder { + lazy val value: Int = { + (System.nanoTime() % 1000).toInt + } + } + + object ObjectHolder { + lazy val value: String = { + System.nanoTime() % 5 match { + case 0 => "abc" + case 1 => "def" + case 2 => "ghi" + case 3 => "jkl" + case 4 => "mno" + } + } + } } diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 1885210a6687..9c1ff1f26763 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -151,7 +151,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { // !!! Part of this logic is duplicated in JSCodeGen.genCompilationUnit claszSymbol.info.decls.foreach { f => - if f.isField && !f.name.is(LazyBitMapName) then + if f.isField && !f.name.is(LazyBitMapName) && !f.name.is(LazyLocalName) then f.setFlag(JavaStatic) } diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index ecdd0ae98803..f8f683a429f6 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -22,7 +22,7 @@ import dotty.tools.dotc.report import tpd._ import StdNames.nme -import NameKinds.LazyBitMapName +import NameKinds.{LazyBitMapName, LazyLocalName} import Names.Name class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap: ReadOnlyMap[Symbol, Set[ClassSymbol]])(using val ctx: Context) { @@ -129,10 +129,11 @@ object DottyBackendInterface { * the new lazy val encoding: https://github.com/lampepfl/dotty/issues/7140 */ def isStaticModuleField(using Context): Boolean = - sym.owner.isStaticModuleClass && sym.isField && !sym.name.is(LazyBitMapName) + sym.owner.isStaticModuleClass && sym.isField && !sym.name.is(LazyBitMapName) && !sym.name.is(LazyLocalName) def isStaticMember(using Context): Boolean = (sym ne NoSymbol) && - (sym.is(JavaStatic) || sym.isScalaStatic || sym.isStaticModuleField) + (sym.is(JavaStatic) || sym.isScalaStatic || sym.isStaticModuleField) + // guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone /** diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 0a9a2b1214b2..0861350c30a9 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -466,13 +466,9 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Private, defn.ObjectType, coord = x.symbol.coord).enteredAfter(this) containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef containerSymbol.addAnnotations(x.symbol.annotations) // pass annotations from original definition - val stat = x.symbol.isStatic - if stat then - containerSymbol.setFlag(JavaStatic) + containerSymbol.removeAnnotation(defn.ScalaStaticAnnot) + containerSymbol.resetFlag(JavaStatic) val getOffset = - if stat then - Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getStaticFieldOffset) - else Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic) val containerTree = ValDef(containerSymbol, nullLiteral) @@ -490,9 +486,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val offset = ref(offsetSymbol.nn) val swapOver = - if stat then - tpd.clsOf(x.symbol.owner.typeRef) - else This(claz) val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offset, swapOver) From e466fa4541002f299bebf301e7872fcbba41a0ea Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 1 Feb 2023 18:19:19 +0100 Subject: [PATCH 14/97] No need to reset JavaStatic as its removed with the amsk --- compiler/src/dotty/tools/dotc/transform/LazyVals.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 0861350c30a9..8d3702190763 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -467,7 +467,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef containerSymbol.addAnnotations(x.symbol.annotations) // pass annotations from original definition containerSymbol.removeAnnotation(defn.ScalaStaticAnnot) - containerSymbol.resetFlag(JavaStatic) val getOffset = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic) val containerTree = ValDef(containerSymbol, nullLiteral) From ef8e8553e6e653f95ca44a692279ead1ad7b71d8 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 1 Feb 2023 18:25:07 +0100 Subject: [PATCH 15/97] Removing getStaticFieldOffset as it's not used anymore --- compiler/src/dotty/tools/dotc/transform/LazyVals.scala | 1 - library/src/scala/runtime/LazyVals.scala | 8 -------- 2 files changed, 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 8d3702190763..e4cb21a279d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -674,7 +674,6 @@ object LazyVals { val cas: TermName = N.cas.toTermName val getOffset: TermName = N.getOffset.toTermName val getOffsetStatic: TermName = "getOffsetStatic".toTermName - val getStaticFieldOffset: TermName = "getStaticFieldOffset".toTermName val getDeclaredField: TermName = "getDeclaredField".toTermName } val flag: TermName = "flag".toTermName diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 5d1e8e74b89d..a75042671efa 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -142,14 +142,6 @@ object LazyVals { r } - def getStaticFieldOffset(field: java.lang.reflect.Field): Long = { - @nowarn - val r = unsafe.staticFieldOffset(field) - if (debug) - println(s"getStaticFieldOffset(${field.getDeclaringClass}, ${field.getName}) = $r") - r - } - def getOffsetStatic(field: java.lang.reflect.Field) = @nowarn val r = unsafe.objectFieldOffset(field) From 2bfbe7553098e050c8403557ff35a75902b8a0b7 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 2 Feb 2023 16:45:19 +0100 Subject: [PATCH 16/97] Revert deletion of getStaticFieldOffset for now --- library/src/scala/runtime/LazyVals.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index a75042671efa..5d1e8e74b89d 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -142,6 +142,14 @@ object LazyVals { r } + def getStaticFieldOffset(field: java.lang.reflect.Field): Long = { + @nowarn + val r = unsafe.staticFieldOffset(field) + if (debug) + println(s"getStaticFieldOffset(${field.getDeclaringClass}, ${field.getName}) = $r") + r + } + def getOffsetStatic(field: java.lang.reflect.Field) = @nowarn val r = unsafe.objectFieldOffset(field) From 41cfb62df29b9cf893eb65034af169e0266a3632 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 3 Feb 2023 15:21:56 +0100 Subject: [PATCH 17/97] Update printing tests to have matching AST --- .../printing/transformed/lazy-vals-new.check | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/printing/transformed/lazy-vals-new.check b/tests/printing/transformed/lazy-vals-new.check index 406417845c20..4b81cd457a38 100644 --- a/tests/printing/transformed/lazy-vals-new.check +++ b/tests/printing/transformed/lazy-vals-new.check @@ -10,19 +10,19 @@ package { @static private def (): Unit = { A.OFFSET$_m_0 = - scala.runtime.LazyVals.getStaticFieldOffset( + scala.runtime.LazyVals.getOffsetStatic( classOf[Object {...}].getDeclaredField("x$lzy1")) () } @static @static val OFFSET$_m_0: Long = - scala.runtime.LazyVals.getStaticFieldOffset( + scala.runtime.LazyVals.getOffsetStatic( classOf[Object {...}].getDeclaredField("x$lzy1")) private def writeReplace(): Object = new scala.runtime.ModuleSerializationProxy(classOf[A]) - @volatile private lazy var x$lzy1: Object = null + @volatile private lazy var x$lzy1: Object = null lazy def x(): Int = { - val result: Object = A#x$lzy1 + val result: Object = A.x$lzy1 if result.isInstanceOf[Int] then scala.Int.unbox(result) else if result.eq(scala.runtime.LazyVals.NullValue) then scala.Int.unbox(null) else scala.Int.unbox(A.x$lzyINIT1()) @@ -30,10 +30,10 @@ package { private def x$lzyINIT1(): Object = while do { - val current: Object = A#x$lzy1 + val current: Object = A.x$lzy1 if current.eq(null) then if - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, null, + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, null, scala.runtime.LazyVals.Evaluating) then { @@ -49,15 +49,15 @@ package { } finally if - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, scala.runtime.LazyVals.Evaluating, result).unary_!() then { val lock: scala.runtime.LazyVals.LazyVals$Waiting = - A#x$lzy1.asInstanceOf[ + A.x$lzy1.asInstanceOf[ scala.runtime.LazyVals.LazyVals$Waiting] - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, - lock, result) + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, lock, + result) lock.countDown() } else () @@ -71,8 +71,8 @@ package { then if current.eq(scala.runtime.LazyVals.Evaluating) then { - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, - current, new scala.runtime.LazyVals.LazyVals$Waiting()) + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, current, + new scala.runtime.LazyVals.LazyVals$Waiting()) () } else From 805c49f2067ff1a0eedfd2bef4b023d908af7e17 Mon Sep 17 00:00:00 2001 From: Vasil Vasilev Date: Thu, 2 Feb 2023 14:13:09 +0100 Subject: [PATCH 18/97] Add support for disabling redirected output in the REPL driver for usage in worksheets in the Scala Plugin for IntelliJ IDEA - Calling `setOut/setErr` in a concurrent environment without any synchronization (such as the Scala compile server in the Scala Plugin for IntelliJ IDEA, which is used to execute Scala 3 worksheets) can lead to unpredictable outcomes where the out/err streams are not restored properly after changing. - This change adds a new default method `redirectOutput` which can be overriden by others to control the redirecting behavior of the REPL driver. --- .../src/dotty/tools/repl/ReplDriver.scala | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index b072d58f6bb7..0f29591e2121 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -187,19 +187,23 @@ class ReplDriver(settings: Array[String], // TODO: i5069 final def bind(name: String, value: Any)(using state: State): State = state + protected def redirectOutput: Boolean = true + // redirecting the output allows us to test `println` in scripted tests private def withRedirectedOutput(op: => State): State = { - val savedOut = System.out - val savedErr = System.err - try { - System.setOut(out) - System.setErr(out) - op - } - finally { - System.setOut(savedOut) - System.setErr(savedErr) - } + if redirectOutput then + val savedOut = System.out + val savedErr = System.err + try { + System.setOut(out) + System.setErr(out) + op + } + finally { + System.setOut(savedOut) + System.setErr(savedErr) + } + else op } private def newRun(state: State, reporter: StoreReporter = newStoreReporter) = { From b36f3192ed3bc75877837b7fa5182d827fc4239e Mon Sep 17 00:00:00 2001 From: Vasil Vasilev Date: Mon, 6 Feb 2023 12:08:05 +0100 Subject: [PATCH 19/97] Add scaladoc documentation for `ReplDriver#redirectOutput` --- compiler/src/dotty/tools/repl/ReplDriver.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 0f29591e2121..905f4f06de08 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -187,6 +187,17 @@ class ReplDriver(settings: Array[String], // TODO: i5069 final def bind(name: String, value: Any)(using state: State): State = state + /** + * Controls whether the `System.out` and `System.err` streams are set to the provided constructor parameter instance + * of [[java.io.PrintStream]] during the execution of the repl. On by default. + * + * Disabling this can be beneficial when executing a repl instance inside a concurrent environment, for example a + * thread pool (such as the Scala compile server in the Scala Plugin for IntelliJ IDEA). + * + * In such environments, indepently executing `System.setOut` and `System.setErr` without any synchronization can + * lead to unpredictable results when restoring the original streams (dependent on the order of execution), leaving + * the Java process in an inconsistent state. + */ protected def redirectOutput: Boolean = true // redirecting the output allows us to test `println` in scripted tests From ff006d0677777d64810cf825fb4871b01b323509 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 11 Feb 2023 15:37:04 +0100 Subject: [PATCH 20/97] Add missing criterion to subtype check Fixes #16850 --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 1 + tests/neg/i16850.check | 10 ++++++++++ tests/neg/i16850.scala | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 tests/neg/i16850.check create mode 100644 tests/neg/i16850.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index cd1e55ef028c..6428c5315263 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -309,6 +309,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling thirdTryNamed(tp2) else ( (tp1.name eq tp2.name) + && !sym1.is(Private) && tp2.isPrefixDependentMemberRef && isSubPrefix(tp1.prefix, tp2.prefix) && tp1.signature == tp2.signature diff --git a/tests/neg/i16850.check b/tests/neg/i16850.check new file mode 100644 index 000000000000..6c9c7f7e0eac --- /dev/null +++ b/tests/neg/i16850.check @@ -0,0 +1,10 @@ +-- [E007] Type Mismatch Error: tests/neg/i16850.scala:7:33 ------------------------------------------------------------- +7 | def add(elm: Y): Unit = list = elm :: list // error + | ^^^ + | Found: (elm : Y) + | Required: Class.this.Y² + | + | where: Y is a type in class Class + | Y² is a type in trait Trait + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i16850.scala b/tests/neg/i16850.scala new file mode 100644 index 000000000000..e7904fcd44e7 --- /dev/null +++ b/tests/neg/i16850.scala @@ -0,0 +1,10 @@ + +trait Trait : + type Y + var list: List[Y] = Nil + +class Class[Y] extends Trait : + def add(elm: Y): Unit = list = elm :: list // error + +object Object extends Class[Int] : + add(42) From f3347dbc802cb4661466028b0a9ddc83a4a1a0a0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 18 Jan 2023 16:52:16 +0100 Subject: [PATCH 21/97] =?UTF-8?q?Avoid=20timeouts=20in=20community?= =?UTF-8?q?=E2=80=93build-C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increased timeout due to timeouts when running on dotty community build --- community-build/community-projects/requests-scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/requests-scala b/community-build/community-projects/requests-scala index 6d4a223bc33d..23b4895710f1 160000 --- a/community-build/community-projects/requests-scala +++ b/community-build/community-projects/requests-scala @@ -1 +1 @@ -Subproject commit 6d4a223bc33def14ae9a4def24a3f5c258451e8e +Subproject commit 23b4895710f17bf892563b28755b225c8be7f7e3 From 8562128549869d4e9aa2db5bf5c215a3a6b49f33 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 22/97] Add changelog for 3.3.0-RC3 --- 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 b3c1c98c47769930ef6108e3f641b5f5509dfabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 17 Feb 2023 13:35:41 +0100 Subject: [PATCH 23/97] Release 3.3.0-RC3 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 75d3e12baf66..4360add9578a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.2.2" - val baseVersion = "3.3.0-RC2" + val baseVersion = "3.3.0-RC3" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.2.2" + val previousDottyVersion = "3.3.0-RC2" object CompatMode { final val BinaryCompatible = 0 From 014be6f46c769cf83f458d2ec5f19d4fb8344548 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 14 Feb 2023 10:36:21 +0100 Subject: [PATCH 24/97] Fix HK quoted pattern type variables The issue was in the encoding into `{ExprMatchModule,TypeMatchModule}.unapply`. Specifically with the `TypeBindings` argument. This arguments holds the list of type variable definitions (`tpd.Bind` trees). We used a `Tuple` to list all the types inside. The problem is that higher-kinded type variables do not conform with the upper bounds of the tuple elements. The solution is to use an HList with any-kinded elements. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 8 ++++++-- .../src/dotty/tools/dotc/core/Definitions.scala | 3 +++ .../tools/dotc/typer/QuotesAndSplices.scala | 4 ++-- .../quoted/runtime/impl/QuoteMatcher.scala | 4 ++-- .../scala/quoted/runtime/impl/QuotesImpl.scala | 4 ++-- .../scala/quoted/runtime/QuoteMatching.scala | 11 ++++++++--- .../hk-quoted-type-patterns/Macro_1.scala | 17 +++++++++++++++++ .../hk-quoted-type-patterns/Test_2.scala | 5 +++++ 8 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala create mode 100644 tests/pos-macros/hk-quoted-type-patterns/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index dd1e46c62223..01d61986dee4 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1498,7 +1498,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } - /** Creates the tuple type tree repesentation of the type trees in `ts` */ + /** Creates the tuple type tree representation of the type trees in `ts` */ def tupleTypeTree(elems: List[Tree])(using Context): Tree = { val arity = elems.length if arity <= Definitions.MaxTupleArity then @@ -1509,10 +1509,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else nestedPairsTypeTree(elems) } - /** Creates the nested pairs type tree repesentation of the type trees in `ts` */ + /** Creates the nested pairs type tree representation of the type trees in `ts` */ def nestedPairsTypeTree(ts: List[Tree])(using Context): Tree = ts.foldRight[Tree](TypeTree(defn.EmptyTupleModule.termRef))((x, acc) => AppliedTypeTree(TypeTree(defn.PairClass.typeRef), x :: acc :: Nil)) + /** Creates the nested higher-kinded pairs type tree representation of the type trees in `ts` */ + def hkNestedPairsTypeTree(ts: List[Tree])(using Context): Tree = + ts.foldRight[Tree](TypeTree(defn.QuoteMatching_KNil.typeRef))((x, acc) => AppliedTypeTree(TypeTree(defn.QuoteMatching_KCons.typeRef), x :: acc :: Nil)) + /** Replaces all positions in `tree` with zero-extent positions */ private def focusPositions(tree: Tree)(using Context): Tree = { val transformer = new tpd.TreeMap { diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ed86050436e8..56409ad050f6 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -865,6 +865,9 @@ class Definitions { @tu lazy val QuoteMatching_ExprMatchModule: Symbol = QuoteMatchingClass.requiredClass("ExprMatchModule") @tu lazy val QuoteMatching_TypeMatch: Symbol = QuoteMatchingClass.requiredMethod("TypeMatch") @tu lazy val QuoteMatching_TypeMatchModule: Symbol = QuoteMatchingClass.requiredClass("TypeMatchModule") + @tu lazy val QuoteMatchingModule: Symbol = requiredModule("scala.quoted.runtime.QuoteMatching") + @tu lazy val QuoteMatching_KNil: Symbol = QuoteMatchingModule.requiredType("KNil") + @tu lazy val QuoteMatching_KCons: Symbol = QuoteMatchingModule.requiredType("KCons") @tu lazy val ToExprModule: Symbol = requiredModule("scala.quoted.ToExpr") @tu lazy val ToExprModule_BooleanToExpr: Symbol = ToExprModule.requiredMethod("BooleanToExpr") diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 2fe5770c5b4b..65d8abfdf6a7 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -364,7 +364,7 @@ trait QuotesAndSplices { * * ``` * case scala.internal.quoted.Expr.unapply[ - * Tuple1[t @ _], // Type binging definition + * KList[t @ _, KNil], // Type binging definition * Tuple2[Type[t], Expr[List[t]]] // Typing the result of the pattern match * ]( * Tuple2.unapply @@ -411,7 +411,7 @@ trait QuotesAndSplices { val replaceBindings = new ReplaceBindings val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen))) - val typeBindingsTuple = tpd.tupleTypeTree(typeBindings.values.toList) + val typeBindingsTuple = tpd.hkNestedPairsTypeTree(typeBindings.values.toList) val replaceBindingsInTree = new TreeMap { private var bindMap = Map.empty[Symbol, Symbol] diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index d85d92de5455..7c952dbbe142 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -121,9 +121,9 @@ object QuoteMatcher { private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env) - def treeMatch(scrutineeTerm: Tree, patternTerm: Tree)(using Context): Option[Tuple] = + def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[Tuple] = given Env = Map.empty - scrutineeTerm =?= patternTerm + scrutineeTree =?= patternTree /** Check that all trees match with `mtch` and concatenate the results with &&& */ private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match { diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 4d08e0582d1d..d1806947fa5d 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3093,14 +3093,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler new TypeImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Type[T]] object ExprMatch extends ExprMatchModule: - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] = + def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] = val scrutineeTree = reflect.asTerm(scrutinee) val patternTree = reflect.asTerm(pattern) treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]] end ExprMatch object TypeMatch extends TypeMatchModule: - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] = + def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] = val scrutineeTree = reflect.TypeTree.of(using scrutinee) val patternTree = reflect.TypeTree.of(using pattern) treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]] diff --git a/library/src/scala/quoted/runtime/QuoteMatching.scala b/library/src/scala/quoted/runtime/QuoteMatching.scala index 2a76143e9868..c95ffe87b5dc 100644 --- a/library/src/scala/quoted/runtime/QuoteMatching.scala +++ b/library/src/scala/quoted/runtime/QuoteMatching.scala @@ -17,7 +17,7 @@ trait QuoteMatching: * - `ExprMatch.unapply('{ f(0, myInt) })('{ f(patternHole[Int], patternHole[Int]) }, _)` * will return `Some(Tuple2('{0}, '{ myInt }))` * - `ExprMatch.unapply('{ f(0, "abc") })('{ f(0, patternHole[Int]) }, _)` - * will return `None` due to the missmatch of types in the hole + * will return `None` due to the mismatch of types in the hole * * Holes: * - scala.quoted.runtime.Patterns.patternHole[T]: hole that matches an expression `x` of type `Expr[U]` @@ -27,7 +27,7 @@ trait QuoteMatching: * @param pattern `Expr[Any]` containing the pattern tree * @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Expr[Ti]`` */ - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: Expr[Any])(using pattern: Expr[Any]): Option[Tup] + def unapply[TypeBindings, Tup <: Tuple](scrutinee: Expr[Any])(using pattern: Expr[Any]): Option[Tup] } val TypeMatch: TypeMatchModule @@ -40,5 +40,10 @@ trait QuoteMatching: * @param pattern `Type[?]` containing the pattern tree * @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Type[Ti]`` */ - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: Type[?])(using pattern: Type[?]): Option[Tup] + def unapply[TypeBindings, Tup <: Tuple](scrutinee: Type[?])(using pattern: Type[?]): Option[Tup] } + +object QuoteMatching: + type KList + type KCons[+H <: AnyKind, +T <: KList] <: KList + type KNil <: KList diff --git a/tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala b/tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala new file mode 100644 index 000000000000..0d2df1504918 --- /dev/null +++ b/tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala @@ -0,0 +1,17 @@ +import scala.quoted._ + +private def impl(x: Expr[Any])(using Quotes): Expr[Unit] = { + x match + case '{ foo[x] } => + assert(Type.show[x] == "scala.Int", Type.show[x]) + case '{ type f[X]; foo[`f`] } => + assert(Type.show[f] == "[A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]", Type.show[f]) + case '{ type f <: AnyKind; foo[`f`] } => + assert(Type.show[f] == "[K >: scala.Nothing <: scala.Any, V >: scala.Nothing <: scala.Any] => scala.collection.immutable.Map[K, V]", Type.show[f]) + case x => throw MatchError(x.show) + '{} +} + +inline def test(inline x: Any): Unit = ${ impl('x) } + +def foo[T <: AnyKind]: Any = ??? diff --git a/tests/pos-macros/hk-quoted-type-patterns/Test_2.scala b/tests/pos-macros/hk-quoted-type-patterns/Test_2.scala new file mode 100644 index 000000000000..3cb9113f2452 --- /dev/null +++ b/tests/pos-macros/hk-quoted-type-patterns/Test_2.scala @@ -0,0 +1,5 @@ +@main +def Test = + test(foo[Int]) + test(foo[List]) + test(foo[Map]) From f0f6bafb87cae7245656059fe72b213958723cea Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 21 Feb 2023 19:01:42 +0100 Subject: [PATCH 25/97] Fix caching issue caused by incorrect isProvisional check A static TypeRef can still be provisional if it's currently being completed (see the logic in `Namer#TypeDefCompleter#typeSig`). Fixes #16950. --- compiler/src/dotty/tools/dotc/core/Types.scala | 5 ++--- tests/pos/i16950.scala | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i16950.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 15b0b00ed0f3..03fc7274beaa 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -118,10 +118,9 @@ object Types { if t.mightBeProvisional then t.mightBeProvisional = t match case t: TypeRef => - !t.currentSymbol.isStatic && { + t.currentSymbol.isProvisional || !t.currentSymbol.isStatic && { (t: Type).mightBeProvisional = false // break cycles - t.symbol.isProvisional - || test(t.prefix, theAcc) + test(t.prefix, theAcc) || t.denot.infoOrCompleter.match case info: LazyType => true case info: AliasingBounds => test(info.alias, theAcc) diff --git a/tests/pos/i16950.scala b/tests/pos/i16950.scala new file mode 100644 index 000000000000..ac95a477136e --- /dev/null +++ b/tests/pos/i16950.scala @@ -0,0 +1,11 @@ +object Foo: + def bar(x : Bar.YOf[Any]): Unit = ??? + +trait K: + type CType <: Bar.YOf[Any] + def foo : K = + val x : CType = ??? + x // was: error: Found: CType, Expected: K + +object Bar: + type YOf[T] = K { type M } From 1a77625064f5285ecf786e465f69476fed51b0d2 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 20 Feb 2023 14:25:28 +0100 Subject: [PATCH 26/97] Fix race condition in new LazyVals --- library/src/scala/runtime/LazyVals.scala | 2 +- tests/run/i16806.check | 2 ++ tests/run/i16806.scala | 42 ++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/run/i16806.check create mode 100644 tests/run/i16806.scala diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 5d1e8e74b89d..416ffc91d34a 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -45,7 +45,7 @@ object LazyVals { /* ------------- Start of public API ------------- */ - sealed trait LazyValControlState + sealed trait LazyValControlState extends Serializable /** * Used to indicate the state of a lazy val that is being diff --git a/tests/run/i16806.check b/tests/run/i16806.check new file mode 100644 index 000000000000..af917347162a --- /dev/null +++ b/tests/run/i16806.check @@ -0,0 +1,2 @@ +Success +Success \ No newline at end of file diff --git a/tests/run/i16806.scala b/tests/run/i16806.scala new file mode 100644 index 000000000000..f45652080458 --- /dev/null +++ b/tests/run/i16806.scala @@ -0,0 +1,42 @@ +import java.util.concurrent.Semaphore +import scala.runtime.LazyVals.Evaluating + +object Repro { + + case object DFBit + final class DFError extends Exception("") + final class DFType[+T](val value: T | DFError) extends AnyVal + + def asIR(dfType: DFType[DFBit.type]): DFBit.type = dfType.value match + case dfTypeIR: DFBit.type => dfTypeIR + case err: DFError => throw new DFError + + object Holder { + val s = new Semaphore(1, false) + final lazy val Bit = { + s.release() + new DFType[DFBit.type](DFBit) + } + } + + @main + def Test = + val a = new Thread() { + override def run(): Unit = + Holder.s.acquire() + val x = Holder.Bit.value + assert(!x.isInstanceOf[Evaluating.type]) + println("Success") + } + val b = new Thread() { + override def run(): Unit = + Holder.s.acquire() + val x = Holder.Bit.value + assert(!x.isInstanceOf[Evaluating.type]) + println("Success") + } + a.start() + b.start() + a.join(300) + b.join(300) +} \ No newline at end of file From 4a7e92bdbfc2f4a0939667e2e408924b81f29af1 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 20 Feb 2023 16:51:05 +0100 Subject: [PATCH 27/97] Do not depend on runtime lib in tests --- tests/run/i16806.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/run/i16806.scala b/tests/run/i16806.scala index f45652080458..0b0dfe1f6b35 100644 --- a/tests/run/i16806.scala +++ b/tests/run/i16806.scala @@ -1,5 +1,4 @@ import java.util.concurrent.Semaphore -import scala.runtime.LazyVals.Evaluating object Repro { @@ -25,14 +24,14 @@ object Repro { override def run(): Unit = Holder.s.acquire() val x = Holder.Bit.value - assert(!x.isInstanceOf[Evaluating.type]) + assert(x.isInstanceOf[DFBit.type]) println("Success") } val b = new Thread() { override def run(): Unit = Holder.s.acquire() val x = Holder.Bit.value - assert(!x.isInstanceOf[Evaluating.type]) + assert(x.isInstanceOf[DFBit.type]) println("Success") } a.start() From 580126233bb98eb2d211b6b0e9cb449de3b8751c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 12:11:24 +0100 Subject: [PATCH 28/97] Disable test for Scalajs --- tests/run/i16806.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run/i16806.scala b/tests/run/i16806.scala index 0b0dfe1f6b35..16c0fb0d3ef5 100644 --- a/tests/run/i16806.scala +++ b/tests/run/i16806.scala @@ -1,3 +1,4 @@ +//scalajs: --skip import java.util.concurrent.Semaphore object Repro { From 81c6d6ebeb4f8ab12dcde2a23e45ce208a4dfa54 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 12:40:41 +0100 Subject: [PATCH 29/97] Add comment describing why LazyValControlState extends Serializable --- library/src/scala/runtime/LazyVals.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 416ffc91d34a..d8c89c7abf28 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -45,6 +45,7 @@ object LazyVals { /* ------------- Start of public API ------------- */ + // This trait extends Serializable to fix #16806 that caused a race condition sealed trait LazyValControlState extends Serializable /** From aa601a1d55095ed533862c8461491a7ac719a011 Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Wed, 15 Feb 2023 16:44:55 +0100 Subject: [PATCH 30/97] Fix #16822 - Ignore synthetic local private - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 19 +++++++-------- .../fatal-warnings/i15503i.scala | 23 ++++++++++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 59878757c39b..6c47c12ac07c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -81,7 +81,7 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then + if tree.symbol.exists then _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then _key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -103,7 +103,7 @@ class CheckUnused extends MiniPhase: override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = _key.unusedDataApply{ud => // do not register the ValDef generated for `object` - if !tree.symbol.is(Module) then + if !tree.symbol.is(Module) then ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } @@ -335,7 +335,7 @@ object CheckUnused: * The optional name will be used to target the right import * as the same element can be imported with different renaming */ - def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = + def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = if !isConstructorOfSynth(sym) && !doNotRegister(sym) then if sym.isConstructor && sym.exists then registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class @@ -371,7 +371,7 @@ object CheckUnused: implicitParamInScope += memDef else explicitParamInScope += memDef - else if currScopeType.top == ScopeType.Local then + else if currScopeType.top == ScopeType.Local then localDefInScope += memDef else if memDef.shouldReportPrivateDef then privateDefInScope += memDef @@ -578,10 +578,10 @@ object CheckUnused: else false - private def usedDefContains(using Context): Boolean = + private def usedDefContains(using Context): Boolean = sym.everySymbol.exists(usedDef.apply) - private def everySymbol(using Context): List[Symbol] = + private def everySymbol(using Context): List[Symbol] = List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists) end extension @@ -614,10 +614,11 @@ object CheckUnused: private def isValidParam(using Context): Boolean = val sym = memDef.symbol (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && - !isSyntheticMainParam(sym) && - !sym.shouldNotReportParamOwner + !isSyntheticMainParam(sym) && + !sym.shouldNotReportParamOwner && + (!sym.exists || !sym.owner.isAllOf(Synthetic | PrivateLocal)) - private def shouldReportPrivateDef(using Context): Boolean = + private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) extension (imp: tpd.Import) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 33e04f34daa8..7eae207d952d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -78,13 +78,13 @@ package foo.test.companionprivate: package foo.test.i16678: def foo(func: Int => String, value: Int): String = func(value) // OK - def run = + def run = println(foo(number => number.toString, value = 5)) // OK println(foo(number => "", value = 5)) // error println(foo(func = number => "", value = 5)) // error println(foo(func = number => number.toString, value = 5)) // OK println(foo(func = _.toString, value = 5)) // OK - + package foo.test.possibleclasses: case class AllCaseClass( k: Int, // OK @@ -93,7 +93,7 @@ package foo.test.possibleclasses: s: Int, // error /* But not these */ val t: Int, // OK private val z: Int // error - ) + ) case class AllCaseUsed( k: Int, // OK @@ -113,7 +113,7 @@ package foo.test.possibleclasses: s: Int, // error val t: Int, // OK private val z: Int // error - ) + ) class AllUsed( k: Int, // OK @@ -124,10 +124,21 @@ package foo.test.possibleclasses: private val z: Int // OK ) { def a = k + y + s + t + z - } + } package foo.test.from.i16675: case class PositiveNumber private (i: Int) // OK object PositiveNumber: - def make(i: Int): Option[PositiveNumber] = //OK + def make(i: Int): Option[PositiveNumber] = //OK Option.when(i >= 0)(PositiveNumber(i)) // OK + +package foo.test.i16822: + enum ExampleEnum { + case Build(context: String) // OK + case List // OK + } + + def demo = { + val x = ExampleEnum.List // OK + println(x) // OK + } From 85fa542c390233350ddee0960133375fe9adff63 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 16 Feb 2023 15:53:07 +0100 Subject: [PATCH 31/97] Register usage of symbols in non-inferred type trees in CheckUnused fixes lampepfl#16930 --- .../tools/dotc/transform/CheckUnused.scala | 59 ++++++++++--------- .../fatal-warnings/i16930.scala | 22 +++++++ 2 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16930.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6c47c12ac07c..663f7f15b96f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -46,12 +46,11 @@ class CheckUnused extends MiniPhase: */ private val _key = Property.Key[UnusedData] - extension (k: Property.Key[UnusedData]) - private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = - ctx.property(_key).foreach(f) - ctx - private def getUnusedData(using Context): Option[UnusedData] = - ctx.property(_key) + private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = + ctx.property(_key).foreach(f) + ctx + private def getUnusedData(using Context): Option[UnusedData] = + ctx.property(_key) override def phaseName: String = CheckUnused.phaseName @@ -71,7 +70,7 @@ class CheckUnused extends MiniPhase: // ========== END + REPORTING ========== override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = - _key.unusedDataApply(ud => reportUnused(ud.getUnused)) + unusedDataApply(ud => reportUnused(ud.getUnused)) tree // ========== MiniPhase Prepare ========== @@ -81,15 +80,15 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then - _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + if tree.symbol.exists then + unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then - _key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) else ctx override def prepareForSelect(tree: tpd.Select)(using Context): Context = - _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) override def prepareForBlock(tree: tpd.Block)(using Context): Context = pushInBlockTemplatePackageDef(tree) @@ -101,7 +100,7 @@ class CheckUnused extends MiniPhase: pushInBlockTemplatePackageDef(tree) override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = - _key.unusedDataApply{ud => + unusedDataApply{ud => // do not register the ValDef generated for `object` if !tree.symbol.is(Module) then ud.registerDef(tree) @@ -109,7 +108,7 @@ class CheckUnused extends MiniPhase: } override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = - _key.unusedDataApply{ ud => + unusedDataApply{ ud => import ud.registerTrivial tree.registerTrivial ud.registerDef(tree) @@ -117,17 +116,17 @@ class CheckUnused extends MiniPhase: } override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = - _key.unusedDataApply{ ud => + unusedDataApply{ ud => if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } override def prepareForBind(tree: tpd.Bind)(using Context): Context = - _key.unusedDataApply(_.registerPatVar(tree)) + unusedDataApply(_.registerPatVar(tree)) override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = - typeTraverser(_key.unusedDataApply).traverse(tree.tpe) + if !tree.isInstanceOf[tpd.InferredTypeTree] then typeTraverser(unusedDataApply).traverse(tree.tpe) ctx // ========== MiniPhase Transform ========== @@ -145,27 +144,27 @@ class CheckUnused extends MiniPhase: tree override def transformValDef(tree: tpd.ValDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree override def transformDefDef(tree: tpd.DefDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree // ---------- MiniPhase HELPERS ----------- private def pushInBlockTemplatePackageDef(tree: tpd.Block | tpd.Template | tpd.PackageDef)(using Context): Context = - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.pushScope(UnusedData.ScopeType.fromTree(tree)) } ctx private def popOutBlockTemplatePackageDef()(using Context): Context = - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.popScope() } ctx @@ -188,7 +187,7 @@ class CheckUnused extends MiniPhase: val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx tree match case imp:tpd.Import => - _key.unusedDataApply(_.registerImport(imp)) + unusedDataApply(_.registerImport(imp)) traverseChildren(tree)(using newCtx) case ident: Ident => prepareForIdent(ident) @@ -198,7 +197,7 @@ class CheckUnused extends MiniPhase: traverseChildren(tree)(using newCtx) case _: (tpd.Block | tpd.Template | tpd.PackageDef) => //! DIFFERS FROM MINIPHASE - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree)(using newCtx)) } case t:tpd.ValDef => @@ -216,9 +215,10 @@ class CheckUnused extends MiniPhase: case t: tpd.Bind => prepareForBind(t) traverseChildren(tree)(using newCtx) + case _: tpd.InferredTypeTree => case t@tpd.TypeTree() => //! DIFFERS FROM MINIPHASE - typeTraverser(_key.unusedDataApply).traverse(t.tpe) + typeTraverser(unusedDataApply).traverse(t.tpe) traverseChildren(tree)(using newCtx) case _ => //! DIFFERS FROM MINIPHASE @@ -228,9 +228,14 @@ class CheckUnused extends MiniPhase: /** This is a type traverser which catch some special Types not traversed by the term traverser above */ private def typeTraverser(dt: (UnusedData => Any) => Unit)(using Context) = new TypeTraverser: - override def traverse(tp: Type): Unit = tp match - case AnnotatedType(_, annot) => dt(_.registerUsed(annot.symbol, None)) - case _ => traverseChildren(tp) + override def traverse(tp: Type): Unit = + if tp.typeSymbol.exists then dt(_.registerUsed(tp.typeSymbol, Some(tp.typeSymbol.name))) + tp match + case AnnotatedType(_, annot) => + dt(_.registerUsed(annot.symbol, None)) + traverseChildren(tp) + case _ => + traverseChildren(tp) /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = diff --git a/tests/neg-custom-args/fatal-warnings/i16930.scala b/tests/neg-custom-args/fatal-warnings/i16930.scala new file mode 100644 index 000000000000..1f6c5bf1a09f --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16930.scala @@ -0,0 +1,22 @@ +// scalac: -Wunused:imports + +trait Outer: + trait Used + trait Unused + +object Test { + val outer: Outer = ??? + import outer.{Used, Unused} // error + def foo(x: Any): Used = x.asInstanceOf[Used] +} + +trait Outer1: + trait UnusedToo1 + trait Unused1 + def unusedToo1: UnusedToo1 + +object Test1 { + val outer1: Outer1 = ??? + import outer1.{Unused1, UnusedToo1} // error // error + def foo() = outer1.unusedToo1 // in this case UnusedToo1 is not used explicitly, only inferred +} From ab28b090dc7e577bb7bf18313cd3c2920f2b70aa Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sat, 18 Feb 2023 16:41:59 +0100 Subject: [PATCH 32/97] Traverse annotations instead of just registering - Traverse the tree of annotations - Update test suits --- .../tools/dotc/transform/CheckUnused.scala | 20 +++++++++---------- .../fatal-warnings/i15503i.scala | 9 +++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 663f7f15b96f..a07bf1e45247 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -28,6 +28,7 @@ import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Types.TermRef import dotty.tools.dotc.core.Types.NameFilter +import dotty.tools.dotc.core.Symbols.Symbol @@ -80,7 +81,7 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then + if tree.symbol.exists then unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -102,6 +103,7 @@ class CheckUnused extends MiniPhase: override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = unusedDataApply{ud => // do not register the ValDef generated for `object` + traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) @@ -111,6 +113,7 @@ class CheckUnused extends MiniPhase: unusedDataApply{ ud => import ud.registerTrivial tree.registerTrivial + traverseAnnotations(tree.symbol) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } @@ -118,11 +121,13 @@ class CheckUnused extends MiniPhase: override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = unusedDataApply{ ud => if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) + traverseAnnotations(tree.symbol) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } override def prepareForBind(tree: tpd.Bind)(using Context): Context = + traverseAnnotations(tree.symbol) unusedDataApply(_.registerPatVar(tree)) override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = @@ -237,6 +242,10 @@ class CheckUnused extends MiniPhase: case _ => traverseChildren(tp) + /** This traverse the annotations of the symbol */ + private def traverseAnnotations(sym: Symbol)(using Context): Unit = + sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) + /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = import CheckUnused.WarnTypes @@ -279,7 +288,6 @@ object CheckUnused: private class UnusedData: import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} - import dotty.tools.dotc.core.Symbols.Symbol import UnusedData.ScopeType /** The current scope during the tree traversal */ @@ -329,11 +337,6 @@ object CheckUnused: execInNewScope popScope() - /** Register all annotations of this symbol's denotation */ - def registerUsedAnnotation(sym: Symbol)(using Context): Unit = - val annotSym = sym.denot.annotations.map(_.symbol) - annotSym.foreach(s => registerUsed(s, None)) - /** * Register a found (used) symbol along with its name * @@ -368,8 +371,6 @@ object CheckUnused: /** Register (or not) some `val` or `def` according to the context, scope and flags */ def registerDef(memDef: tpd.MemberDef)(using Context): Unit = - // register the annotations for usage - registerUsedAnnotation(memDef.symbol) if memDef.isValidMemberDef then if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then @@ -383,7 +384,6 @@ object CheckUnused: /** Register pattern variable */ def registerPatVar(patvar: tpd.Bind)(using Context): Unit = - registerUsedAnnotation(patvar.symbol) if !patvar.symbol.isUnusedAnnot then patVarsInScope += patvar diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 7eae207d952d..ccf9344319d2 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -142,3 +142,12 @@ package foo.test.i16822: val x = ExampleEnum.List // OK println(x) // OK } + +package foo.test.i16877: + import scala.collection.immutable.HashMap // OK + import scala.annotation.StaticAnnotation // OK + + class ExampleAnnotation(val a: Object) extends StaticAnnotation // OK + + @ExampleAnnotation(new HashMap()) // OK + class Test //OK From d4f8c740c432b0d1ef328235ee651ce91a09094e Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sat, 18 Feb 2023 17:16:38 +0100 Subject: [PATCH 33/97] Ignore parameter of accessors - Do not report parameter of accessors - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 2 +- .../fatal-warnings/i15503e.scala | 3 ++ .../fatal-warnings/i15503i.scala | 43 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a07bf1e45247..a1ccccdb12e2 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -621,7 +621,7 @@ object CheckUnused: (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && !isSyntheticMainParam(sym) && !sym.shouldNotReportParamOwner && - (!sym.exists || !sym.owner.isAllOf(Synthetic | PrivateLocal)) + (!sym.exists || !(sym.owner.isAllOf(Synthetic | PrivateLocal) || sym.owner.is(Accessor))) private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 79112942a205..cd56587327cd 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -52,3 +52,6 @@ package foo.test.trivial: def f77(x: Int) = foo // error } object Y + +package foo.test.i16955: + class S(var r: String) // OK \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index ccf9344319d2..82fb9acf7ace 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -126,6 +126,49 @@ package foo.test.possibleclasses: def a = k + y + s + t + z } +package foo.test.possibleclasses.withvar: + case class AllCaseClass( + k: Int, // OK + private var y: Int // OK /* Kept as it can be taken from pattern */ + )( + s: Int, // error /* But not these */ + var t: Int, // OK + private var z: Int // error + ) + + case class AllCaseUsed( + k: Int, // OK + private var y: Int // OK + )( + s: Int, // OK + var t: Int, // OK + private var z: Int // OK + ) { + def a = k + y + s + t + z + } + + class AllClass( + k: Int, // error + private var y: Int // error + )( + s: Int, // error + var t: Int, // OK + private var z: Int // error + ) + + class AllUsed( + k: Int, // OK + private var y: Int // OK + )( + s: Int, // OK + var t: Int, // OK + private var z: Int // OK + ) { + def a = k + y + s + t + z + } + + + package foo.test.from.i16675: case class PositiveNumber private (i: Int) // OK object PositiveNumber: From 49686f87ba6ab3695a4192c155eff9715bc79dd9 Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sun, 19 Feb 2023 12:35:23 +0100 Subject: [PATCH 34/97] Improve override detection in CheckUnused - CheckUnused detects override from base type in addition of `override` flag - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 19 +++++++++++++------ .../fatal-warnings/i15503e.scala | 14 +++++++++++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a1ccccdb12e2..49ce64b00b88 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -574,12 +574,14 @@ object CheckUnused: private def shouldNotReportParamOwner(using Context): Boolean = if sym.exists then val owner = sym.owner - trivialDefs(owner) || - owner.is(Flags.Override) || + trivialDefs(owner) || // is a trivial def owner.isPrimaryConstructor || - owner.annotations.exists ( + owner.annotations.exists ( // @depreacated _.symbol == ctx.definitions.DeprecatedAnnot - ) + ) || + owner.isAllOf(Synthetic | PrivateLocal) || + owner.is(Accessor) || + owner.isOverriden else false @@ -589,6 +591,11 @@ object CheckUnused: private def everySymbol(using Context): List[Symbol] = List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists) + /** 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) || + (if sym.exists then sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists) else false) + end extension extension (defdef: tpd.DefDef) @@ -620,8 +627,8 @@ object CheckUnused: val sym = memDef.symbol (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && !isSyntheticMainParam(sym) && - !sym.shouldNotReportParamOwner && - (!sym.exists || !(sym.owner.isAllOf(Synthetic | PrivateLocal) || sym.owner.is(Accessor))) + !sym.shouldNotReportParamOwner + private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index cd56587327cd..56aec702a39e 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -54,4 +54,16 @@ package foo.test.trivial: object Y package foo.test.i16955: - class S(var r: String) // OK \ No newline at end of file + class S(var r: String) // OK + +package foo.test.i16865: + trait Foo: + def fn(a: Int, b: Int): Int // OK + trait Bar extends Foo + + object Ex extends Bar: + def fn(a: Int, b: Int): Int = b + 3 // OK + + object Ex2 extends Bar: + override def fn(a: Int, b: Int): Int = b + 3 // OK + From e03fa1b7fef5a6d5671208ac6b7bf1b57f935ec1 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 16:03:10 +0100 Subject: [PATCH 35/97] WUnused: Fix unused warnining in synthetic symbols --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 ++++++-- tests/neg-custom-args/fatal-warnings/i16925.scala | 8 ++++++++ tests/neg-custom-args/fatal-warnings/i16926.scala | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16925.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i16926.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 49ce64b00b88..9f3f5aded50c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -7,13 +7,13 @@ import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.config.ScalaSettings import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Decorators.{em, i} -import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.Property +import dotty.tools.dotc.util.{Property, SourcePosition, SrcPos} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Types.TypeTraverser import dotty.tools.dotc.core.Types.Type @@ -302,6 +302,7 @@ object CheckUnused: * See the `isAccessibleAsIdent` extension method below in the file */ private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]()) + private val usedInPosition = MutSet[(SrcPos, Name)]() /* unused import collected during traversal */ private val unusedImport = MutSet[ImportSelector]() @@ -351,6 +352,7 @@ object CheckUnused: usedInScope.top += ((sym, sym.isAccessibleAsIdent, name)) usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name)) usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name)) + name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ def addIgnoredUsage(sym: Symbol)(using Context): Unit = @@ -455,6 +457,7 @@ object CheckUnused: if ctx.settings.WunusedHas.locals then localDefInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.LocalDefs).toList else Nil @@ -483,6 +486,7 @@ object CheckUnused: if ctx.settings.WunusedHas.patvars then patVarsInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.PatVars).toList else Nil diff --git a/tests/neg-custom-args/fatal-warnings/i16925.scala b/tests/neg-custom-args/fatal-warnings/i16925.scala new file mode 100644 index 000000000000..5cc94f53cdd4 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16925.scala @@ -0,0 +1,8 @@ +// scalac: -Wunused:all + +def hello = + for { + i <- 1 to 2 if true + _ = println(i) // OK + } yield () + diff --git a/tests/neg-custom-args/fatal-warnings/i16926.scala b/tests/neg-custom-args/fatal-warnings/i16926.scala new file mode 100644 index 000000000000..23f167f4ce30 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16926.scala @@ -0,0 +1,7 @@ +// scalac: -Wunused:all + +def hello(): Unit = + for { + i <- (0 to 10).toList + (a, b) = "hello" -> "world" // OK + } yield println(s"$a $b") From 606608a7935630be69a0b9e4fa149ce0e3bc55fa Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Feb 2023 12:55:33 +0100 Subject: [PATCH 36/97] Move tests --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 14 ++++++++++++++ tests/neg-custom-args/fatal-warnings/i16925.scala | 8 -------- tests/neg-custom-args/fatal-warnings/i16926.scala | 7 ------- 3 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 tests/neg-custom-args/fatal-warnings/i16925.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i16926.scala diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 82fb9acf7ace..ab83e1dafb3b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -194,3 +194,17 @@ package foo.test.i16877: @ExampleAnnotation(new HashMap()) // OK class Test //OK + +package foo.test.i16926: + def hello(): Unit = + for { + i <- (0 to 10).toList + (a, b) = "hello" -> "world" // OK + } yield println(s"$a $b") + +package foo.test.i16925: + def hello = + for { + i <- 1 to 2 if true + _ = println(i) // OK + } yield () \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i16925.scala b/tests/neg-custom-args/fatal-warnings/i16925.scala deleted file mode 100644 index 5cc94f53cdd4..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i16925.scala +++ /dev/null @@ -1,8 +0,0 @@ -// scalac: -Wunused:all - -def hello = - for { - i <- 1 to 2 if true - _ = println(i) // OK - } yield () - diff --git a/tests/neg-custom-args/fatal-warnings/i16926.scala b/tests/neg-custom-args/fatal-warnings/i16926.scala deleted file mode 100644 index 23f167f4ce30..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i16926.scala +++ /dev/null @@ -1,7 +0,0 @@ -// scalac: -Wunused:all - -def hello(): Unit = - for { - i <- (0 to 10).toList - (a, b) = "hello" -> "world" // OK - } yield println(s"$a $b") From b050bdaf495f0e04b77fb60e3ed107176be75dcd Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Feb 2023 17:54:21 +0100 Subject: [PATCH 37/97] Remove unused import --- 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 9f3f5aded50c..e7e6e1c4952c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.{Property, SourcePosition, SrcPos} +import dotty.tools.dotc.util.{Property, SrcPos} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Types.TypeTraverser import dotty.tools.dotc.core.Types.Type From 2d41b4624a1a805137e55de1a7d7dd4acd921ffe Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 13 Mar 2023 15:11:53 +0100 Subject: [PATCH 38/97] Fix WUnused with indents in derived code --- .../tools/dotc/transform/CheckUnused.scala | 14 +++++++------- .../fatal-warnings/i15503i.scala | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index e7e6e1c4952c..c0ea483efea9 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -15,19 +15,14 @@ import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} import dotty.tools.dotc.core.Mode -import dotty.tools.dotc.core.Types.TypeTraverser -import dotty.tools.dotc.core.Types.Type -import dotty.tools.dotc.core.Types.AnnotatedType +import dotty.tools.dotc.core.Types.{AnnotatedType, ConstantType, NoType, TermRef, Type, TypeTraverser} import dotty.tools.dotc.core.Flags.flagsString import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.transform.MegaPhase.MiniPhase import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Definitions -import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.NameKinds.WildcardParamName -import dotty.tools.dotc.core.Types.TermRef -import dotty.tools.dotc.core.Types.NameFilter import dotty.tools.dotc.core.Symbols.Symbol @@ -82,6 +77,12 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then + val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) + for { + prefix <- prefixes + } { + unusedDataApply(_.registerUsed(prefix.classSymbol, None)) + } unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -409,7 +410,6 @@ object CheckUnused: val kept = used.filterNot { t => val (sym, isAccessible, optName) = t // keep the symbol for outer scope, if it matches **no** import - // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index ab83e1dafb3b..9f8416146af4 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -207,4 +207,21 @@ package foo.test.i16925: for { i <- 1 to 2 if true _ = println(i) // OK - } yield () \ No newline at end of file + } yield () + +package foo.test.i16679: + object myPackage: + trait CaseClassName[A]: + def name: String + object CaseClassName: + trait CaseClassByStringName[A] extends CaseClassName[A] + import scala.deriving.Mirror + object CaseClassByStringName: + inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassByStringName[A] = + new CaseClassByStringName[A]: + def name: String = A.toString + + object secondPackage: + import myPackage.CaseClassName // OK + case class CoolClass(i: Int) derives CaseClassName.CaseClassByStringName + println(summon[CaseClassName[CoolClass]].name) From 87d9e947b95656c33890061a7ce2d3e7a5aa8758 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 13 Mar 2023 15:38:12 +0100 Subject: [PATCH 39/97] Add failsafe for a case where prefixes in CheckUnused/prepareIndent formed an infinite cycle --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index c0ea483efea9..66b0876668be 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -78,6 +78,7 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) + .take(10) // Failsafe for the odd case if there was an infinite cycle for { prefix <- prefixes } { From 8bdef2ff1d6e7189f5ee959ba0b4ac66f7713c47 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 14 Mar 2023 15:53:10 +0100 Subject: [PATCH 40/97] Fix for formatting and traverse call of inlined tree in wunused --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 66b0876668be..35153dbf66e9 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.ast.tpd.TreeTraverser +import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser} import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.config.ScalaSettings @@ -59,6 +59,7 @@ class CheckUnused extends MiniPhase: // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + println(tree) val data = UnusedData() val fresh = ctx.fresh.setProperty(_key, data) fresh @@ -75,15 +76,16 @@ class CheckUnused extends MiniPhase: traverser.traverse(tree) ctx + def prepareForInlined(tree: Inlined)(using Context): Context = + traverser.traverse(tree.call) + ctx + override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) .take(10) // Failsafe for the odd case if there was an infinite cycle - for { - prefix <- prefixes - } { + for prefix <- prefixes do unusedDataApply(_.registerUsed(prefix.classSymbol, None)) - } unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) From 813a43b9ec34e59250d8d28b4d9fac777b1e44a9 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 14 Mar 2023 16:11:07 +0100 Subject: [PATCH 41/97] Add test for wunused Inlined call --- .../tools/dotc/transform/CheckUnused.scala | 3 +-- .../fatal-warnings/i15503i.scala | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 35153dbf66e9..5e4ed6f6e0df 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -59,7 +59,6 @@ class CheckUnused extends MiniPhase: // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = - println(tree) val data = UnusedData() val fresh = ctx.fresh.setProperty(_key, data) fresh @@ -76,7 +75,7 @@ class CheckUnused extends MiniPhase: traverser.traverse(tree) ctx - def prepareForInlined(tree: Inlined)(using Context): Context = + override def prepareForInlined(tree: tpd.Inlined)(using Context): Context = traverser.traverse(tree.call) ctx diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 9f8416146af4..3dd4d1fc61e7 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -209,7 +209,7 @@ package foo.test.i16925: _ = println(i) // OK } yield () -package foo.test.i16679: +package foo.test.i16679a: object myPackage: trait CaseClassName[A]: def name: String @@ -225,3 +225,23 @@ package foo.test.i16679: import myPackage.CaseClassName // OK case class CoolClass(i: Int) derives CaseClassName.CaseClassByStringName println(summon[CaseClassName[CoolClass]].name) + +package foo.test.i16679b: + object myPackage: + trait CaseClassName[A]: + def name: String + + object CaseClassName: + import scala.deriving.Mirror + inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassName[A] = + new CaseClassName[A]: + def name: String = A.toString + + object Foo: + given x: myPackage.CaseClassName[secondPackage.CoolClass] = null + + object secondPackage: + import myPackage.CaseClassName // OK + import Foo.x + case class CoolClass(i: Int) + println(summon[myPackage.CaseClassName[CoolClass]]) From 06acf90c86e8285b510746556f2b87671a742ebf Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 7 Mar 2023 15:18:52 +0100 Subject: [PATCH 42/97] WUnused: Fix for symbols with synthetic names and unused transparent inlines --- .../tools/dotc/transform/CheckUnused.scala | 27 +++++++++++++++++- .../fatal-warnings/i15503i.scala | 28 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5e4ed6f6e0df..cc51d9bfbe64 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Decorators.{em, i} import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.report +import dotty.tools.dotc.{ast, report} import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} @@ -432,6 +432,20 @@ object CheckUnused: else exists } + + // not report unused transparent inline imports + for { + imp <- imports + sel <- imp.selectors + } { + if unusedImport.contains(sel) then + val tpd.Import(qual, _) = imp + val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) + val isTransparentAndInline = importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) + if isTransparentAndInline then + unusedImport -= sel + } + // if there's an outer scope if usedInScope.nonEmpty then // we keep the symbols not referencing an import in this scope @@ -450,6 +464,7 @@ object CheckUnused: */ def getUnused(using Context): UnusedResult = popScope() + val sortedImp = if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList @@ -460,6 +475,7 @@ object CheckUnused: localDefInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.LocalDefs).toList else Nil @@ -467,6 +483,7 @@ object CheckUnused: if ctx.settings.WunusedHas.explicits then explicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ExplicitParams).toList else Nil @@ -474,6 +491,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -481,6 +499,7 @@ object CheckUnused: if ctx.settings.WunusedHas.privates then privateDefInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.PrivateMembers).toList else Nil @@ -488,6 +507,7 @@ object CheckUnused: if ctx.settings.WunusedHas.patvars then patVarsInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.PatVars).toList else @@ -500,6 +520,11 @@ object CheckUnused: end getUnused //============================ HELPERS ==================================== + /** + * Heuristic to detect synthetic suffixes in names of symbols + */ + private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = + symbol.name.mangledString.contains("$") /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 3dd4d1fc61e7..9ac2ec5ef622 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -209,6 +209,34 @@ package foo.test.i16925: _ = println(i) // OK } yield () +package foo.test.i16863a: + import scala.quoted.* + def fn(using Quotes) = + val x = Expr(1) + '{ $x + 2 } // OK + +package foo.test.i16863b: + import scala.quoted.* + def fn[A](using Quotes, Type[A]) = // OK + val numeric = Expr.summon[Numeric[A]].getOrElse(???) + '{ $numeric.fromInt(3) } // OK + +package foo.test.i16863c: + import scala.quoted.* + def fn[A](expr: Expr[Any])(using Quotes) = + val imp = expr match + case '{ ${ _ }: a } => Expr.summon[Numeric[a]] // OK + println(imp) + +package foo.test.i16863d: + import scala.quoted.* + import scala.compiletime.asMatchable // OK + def fn[A](using Quotes, Type[A]) = + import quotes.reflect.* + val imp = TypeRepr.of[A].widen.asMatchable match + case Refinement(_,_,_) => () + println(imp) + package foo.test.i16679a: object myPackage: trait CaseClassName[A]: From 0f6c42e9630402a58d0bccfb2e06d9badfb479e3 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 8 Mar 2023 13:24:54 +0100 Subject: [PATCH 43/97] Adjust assertions in test --- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 4 ++-- tests/neg-custom-args/fatal-warnings/i15503b.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index f04129a19e48..18aa6879eeba 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -100,9 +100,9 @@ trait Anonymous { trait Context[A] trait Implicits { def f[A](implicit ctx: Context[A]) = answer // error - def g[A: Context] = answer // error + def g[A: Context] = answer // OK } -class Bound[A: Context] // error +class Bound[A: Context] // OK object Answers { def answer: Int = 42 } diff --git a/tests/neg-custom-args/fatal-warnings/i15503b.scala b/tests/neg-custom-args/fatal-warnings/i15503b.scala index 19bcd01a8dde..8a4a055150f9 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503b.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503b.scala @@ -75,7 +75,7 @@ package foo.scala2.tests: object Types { def l1() = { - object HiObject { def f = this } // error + object HiObject { def f = this } // OK class Hi { // error def f1: Hi = new Hi def f2(x: Hi) = x From fbc65010bffac8fe6de36b958216fcfd430e1770 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 10 Mar 2023 17:36:15 +0100 Subject: [PATCH 44/97] Check if import contains transparent inline in registerImport --- .../tools/dotc/transform/CheckUnused.scala | 29 +++++++++---------- .../fatal-warnings/i15503f.scala | 2 +- .../fatal-warnings/i15503g.scala | 4 +-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index cc51d9bfbe64..9b2fd122f68a 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Decorators.{em, i} import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.{ast, report} +import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} @@ -368,7 +368,7 @@ object CheckUnused: /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = - if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum then + if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then impInScope.top += imp unusedImport ++= imp.selectors.filter { s => !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) @@ -433,19 +433,6 @@ object CheckUnused: exists } - // not report unused transparent inline imports - for { - imp <- imports - sel <- imp.selectors - } { - if unusedImport.contains(sel) then - val tpd.Import(qual, _) = imp - val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) - val isTransparentAndInline = importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - if isTransparentAndInline then - unusedImport -= sel - } - // if there's an outer scope if usedInScope.nonEmpty then // we keep the symbols not referencing an import in this scope @@ -520,6 +507,18 @@ object CheckUnused: end getUnused //============================ HELPERS ==================================== + + /** + * Checks if import selects a def that is transparent and inline + */ + private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = + (for { + sel <- imp.selectors + } yield { + val qual = imp.expr + val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) + importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) + }).exists(identity) /** * Heuristic to detect synthetic suffixes in names of symbols */ diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index db695da3490b..d36cd01be74e 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,7 +5,7 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // error +def f3(a: Int)(using Int) = a // OK def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index d4daea944184..a0822e7e1611 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -5,8 +5,8 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = default_int // error -def f3(a: Int)(using Int) = a // error -def f4(a: Int)(using Int) = default_int // error // error +def f3(a: Int)(using Int) = a // OK +def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // error def f7(a: Int)(using Int) = summon[Int] + a // OK From 4070dbda7b0aaccaff8a577b1a2708f3bba753a4 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Mar 2023 17:32:00 +0200 Subject: [PATCH 45/97] Warn for synthetic using/givens with wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503f.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 9b2fd122f68a..bf1ec37ebab4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -478,7 +478,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol)) + .filterNot(d => containsSyntheticSuffix(d.symbol) && !d.rawMods.is(Given)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index d36cd01be74e..db695da3490b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,7 +5,7 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // OK +def f3(a: Int)(using Int) = a // error def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK From 4644e5ed47ed52036869a9c424772c47500a4586 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Mar 2023 19:29:09 +0200 Subject: [PATCH 46/97] Wunused: only filter out non-zero span-length givens --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index bf1ec37ebab4..665c0b4284ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -478,7 +478,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol) && !d.rawMods.is(Given)) + .filterNot(d => containsSyntheticSuffix(d.symbol) && (!d.rawMods.is(Given) || hasZeroLengthSpan(d.symbol))) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -519,11 +519,18 @@ object CheckUnused: val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) }).exists(identity) + /** * Heuristic to detect synthetic suffixes in names of symbols */ private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = symbol.name.mangledString.contains("$") + + /** + * Heuristic to detect generated symbols by checking if symbol has zero length span in source + */ + private def hasZeroLengthSpan(symbol: Symbol)(using Context): Boolean = + symbol.span.end - symbol.span.start == 0 /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package From b72eade34401c963db8fdcf85799d4d9dc1fc9f8 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 14:52:46 +0200 Subject: [PATCH 47/97] Skip all symbols with $ in name in Wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 665c0b4284ca..f960f7b9e60c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -478,7 +478,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol) && (!d.rawMods.is(Given) || hasZeroLengthSpan(d.symbol))) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -526,11 +526,6 @@ object CheckUnused: private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = symbol.name.mangledString.contains("$") - /** - * Heuristic to detect generated symbols by checking if symbol has zero length span in source - */ - private def hasZeroLengthSpan(symbol: Symbol)(using Context): Boolean = - symbol.span.end - symbol.span.start == 0 /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package From 432e829d3bdae27a2f62e18fb8878c74c0676ceb Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 15:51:50 +0200 Subject: [PATCH 48/97] Add a failing case with named using to test Wunused:implicits --- tests/neg-custom-args/fatal-warnings/i15503f.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index db695da3490b..67c595d74f40 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,8 +5,9 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // error -def f4(a: Int)(using Int) = default_int // error +def f3(a: Int)(using Int) = a // OK +def f4(a: Int)(using Int) = default_int // OK def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK +def f8(a: Int)(using foo: Int) = a // error From 24080f11f1799f33acc94524c3e215234d02549a Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 16:10:21 +0200 Subject: [PATCH 49/97] Replace for with exists in isTransparentInline in WUNused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index f960f7b9e60c..d7c88a1fca40 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -512,13 +512,11 @@ object CheckUnused: * Checks if import selects a def that is transparent and inline */ private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = - (for { - sel <- imp.selectors - } yield { + imp.selectors.exists { sel => val qual = imp.expr val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - }).exists(identity) + } /** * Heuristic to detect synthetic suffixes in names of symbols From 7cbdadf4858b75f76edfbadb4012eff6f663152f Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 19:19:13 +0200 Subject: [PATCH 50/97] Skip extension method params in WUnused --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503g.scala | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index d7c88a1fca40..65ab7f12189f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -651,7 +651,7 @@ object CheckUnused: extension (memDef: tpd.MemberDef) private def isValidMemberDef(using Context): Boolean = - !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard + !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard && !memDef.symbol.owner.is(Extension) private def isValidParam(using Context): Boolean = val sym = memDef.symbol diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index a0822e7e1611..8b3fd7561a4b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -12,4 +12,11 @@ def f7(a: Int)(using Int) = summon[Int] + a // OK /* --- Trivial method check --- */ def g1(x: Int) = 1 // OK -def g2(x: Int) = ??? // OK \ No newline at end of file +def g2(x: Int) = ??? // OK + +package foo.test.i17101: + type Test[A] = A + extension[A] (x: Test[A]) { // OK + def value: A = x + def causesIssue: Unit = println("oh no") + } From ac0603346e0e0297f85abd11de95e500f7c2cc5a Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Mar 2023 16:49:09 +0200 Subject: [PATCH 51/97] Fix wunused false positive when deriving alias type --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 ++++++++++-- libste | 0 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 libste diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 65ab7f12189f..80349bf1f0c7 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -589,14 +589,22 @@ object CheckUnused: /** Given an import and accessibility, return an option of selector that match import<->symbol */ private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp - val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym) + val dealiasedSym = dealias(sym) + val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).map(dealias).contains(dealiasedSym) 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 = sels.flatMap(sel => qual.tpe.member(sym.name).alternatives.map(m => (sel, m.symbol))).collect { + case (sel, sym) if dealias(sym) == dealiasedSym => sel + }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) if qualHasSymbol && !isAccessible && sym.exists then - selector.orElse(wildcard) // selector with name or wildcard (or given) + selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None + private def dealias(symbol: Symbol)(using Context): Symbol = + if(symbol.isType && symbol.asType.denot.isAliasType) then + symbol.asType.typeRef.dealias.typeSymbol + else symbol /** Annotated with @unused */ private def isUnusedAnnot(using Context): Boolean = sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot) diff --git a/libste b/libste new file mode 100644 index 000000000000..e69de29bb2d1 From 41e74189e4ce0c2fb1fe8265546199d4e76e8ae2 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 3 Apr 2023 15:53:05 +0200 Subject: [PATCH 52/97] Fix wunused for deriving alias type that has a different name --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 7 +++++-- libste | 0 tests/neg-custom-args/fatal-warnings/i15503i.scala | 11 +++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) delete mode 100644 libste diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 80349bf1f0c7..4a6109e3ffa0 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -590,9 +590,12 @@ object CheckUnused: private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) - val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).map(dealias).contains(dealiasedSym) + 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 allSelections = typeSelections ::: termSelections :::qual.tpe.member(sym.name).alternatives + val qualHasSymbol = allSelections.map(_.symbol).map(dealias).contains(dealiasedSym) 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 = sels.flatMap(sel => qual.tpe.member(sym.name).alternatives.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = sels.flatMap(sel => allSelections.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) diff --git a/libste b/libste deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 9ac2ec5ef622..a76f96b3c89b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -273,3 +273,14 @@ package foo.test.i16679b: import Foo.x case class CoolClass(i: Int) println(summon[myPackage.CaseClassName[CoolClass]]) + +package foo.test.i17156: + package a: + trait Foo[A] + + package b: + type Xd = Foo + + package c: + import b.Xd + trait Z derives Xd From 8262192141c486bfe13f75a72903b26645df6c83 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 3 Apr 2023 19:45:58 +0200 Subject: [PATCH 53/97] Fix test for wunused alias deriving --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index a76f96b3c89b..737e5f0739ca 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -277,9 +277,12 @@ package foo.test.i16679b: package foo.test.i17156: package a: trait Foo[A] + object Foo: + inline def derived[T]: Foo[T] = new Foo{} package b: - type Xd = Foo + import a.Foo + type Xd[A] = Foo[A] package c: import b.Xd From fd70247d37d44e2599e131a989913c08686571d0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 4 Apr 2023 14:43:12 +0200 Subject: [PATCH 54/97] Fix selecting unaliased selector in wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 4a6109e3ffa0..7062478305fa 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -590,12 +590,13 @@ object CheckUnused: private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) + 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 allSelections = typeSelections ::: termSelections :::qual.tpe.member(sym.name).alternatives - val qualHasSymbol = allSelections.map(_.symbol).map(dealias).contains(dealiasedSym) + val selectionsToDealias = typeSelections ::: termSelections + val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) 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 = sels.flatMap(sel => allSelections.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) @@ -604,6 +605,7 @@ object CheckUnused: else None + private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then symbol.asType.typeRef.dealias.typeSymbol From ec298fa4e37781aa6cf6eef860771884cd33fd46 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 10 Apr 2023 19:41:15 +0200 Subject: [PATCH 55/97] Dealias only conditionally when symbol is derived val type in wunused --- .../tools/dotc/transform/CheckUnused.scala | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 7062478305fa..5a178ff2ec1f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -24,7 +24,7 @@ import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Symbols.Symbol - +import dotty.tools.dotc.core.StdNames.nme /** @@ -109,6 +109,9 @@ class CheckUnused extends MiniPhase: traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then ud.registerDef(tree) + if tree.name.mangledString.startsWith(nme.derived.mangledString + "$") + && tree.typeOpt != NoType then + ud.registerUsed(tree.typeOpt.typeSymbol, None, true) ud.addIgnoredUsage(tree.symbol) } @@ -304,7 +307,7 @@ object CheckUnused: * * See the `isAccessibleAsIdent` extension method below in the file */ - private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]()) + private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name], Boolean)]()) private val usedInPosition = MutSet[(SrcPos, Name)]() /* unused import collected during traversal */ private val unusedImport = MutSet[ImportSelector]() @@ -347,14 +350,14 @@ object CheckUnused: * The optional name will be used to target the right import * as the same element can be imported with different renaming */ - def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = + def registerUsed(sym: Symbol, name: Option[Name], isDerived: Boolean = false)(using Context): Unit = if !isConstructorOfSynth(sym) && !doNotRegister(sym) then if sym.isConstructor && sym.exists then registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class else - usedInScope.top += ((sym, sym.isAccessibleAsIdent, name)) - usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name)) - usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name)) + usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived)) + usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived)) + usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived)) name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ @@ -408,15 +411,15 @@ object CheckUnused: // used symbol in this scope val used = usedInScope.pop().toSet // used imports in this scope - val imports = impInScope.pop().toSet + val imports = impInScope.pop() val kept = used.filterNot { t => - val (sym, isAccessible, optName) = t + val (sym, isAccessible, optName, isDerived) = t // keep the symbol for outer scope, if it matches **no** import // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None val exists = imports.exists { imp => - sym.isInImport(imp, isAccessible, optName) match + sym.isInImport(imp, isAccessible, optName, isDerived) match case None => false case optSel@Some(sel) if sel.isWildcard => if selWildCard.isEmpty then selWildCard = optSel @@ -587,7 +590,7 @@ object CheckUnused: } /** Given an import and accessibility, return an option of selector that match import<->symbol */ - private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = + private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) val simpleSelections = qual.tpe.member(sym.name).alternatives @@ -596,9 +599,9 @@ object CheckUnused: val selectionsToDealias = typeSelections ::: termSelections val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) 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 = sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel - }.headOption + }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) if qualHasSymbol && !isAccessible && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) From 87f8449af369ef933431bf2e4b5c6549847ed65f Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 19:09:47 +0200 Subject: [PATCH 56/97] Fix WUnused for accessible symbols that are renamed --- .../dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503i.scala | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5a178ff2ec1f..fcbb61ab90e2 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,7 +603,7 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && !isAccessible && sym.exists then + if qualHasSymbol && (!isAccessible || symName.exists(_ != sym.name)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 737e5f0739ca..fbdf47dae17a 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -287,3 +287,17 @@ package foo.test.i17156: package c: import b.Xd trait Z derives Xd + +package foo.test.i17117: + package example { + object test1 { + val test = "test" + } + + object test2 { + + import example.test1 as t1 + + val test = t1.test + } + } \ No newline at end of file From 0d2977e856b1ee6b7b5e5db0bac05ff05dd8fdc3 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 14:35:53 +0200 Subject: [PATCH 57/97] Compare simple name and handle NO_NAME case in WUnused --- 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 fcbb61ab90e2..5c5f382de1b2 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,7 +603,7 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || symName.exists(_ != sym.name)) && sym.exists then + if qualHasSymbol && (!isAccessible || (sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName))) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None From c6a6656c7002ef6b3da108d2d35fdeaee4a2adfd Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 17:00:18 +0200 Subject: [PATCH 58/97] Extracted isRenamedSymbol def --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5c5f382de1b2..cd1a21ece440 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,11 +603,13 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || (sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName))) && sym.exists then + if qualHasSymbol && (!isAccessible || isRenamedSymbol(sym, symName)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None + private def isRenamedSymbol(sym: Symbol, symNameInScope: Option[Name]) = + sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName) private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then From 79b87a06f92e7f549ac3bab0d1a6cb5ca494e77f Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 17:16:39 +0200 Subject: [PATCH 59/97] Fix isRenamedSymbol method in WUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index cd1a21ece440..04f993e4c805 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,13 +603,13 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || isRenamedSymbol(sym, symName)) && sym.exists then + if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None - private def isRenamedSymbol(sym: Symbol, symNameInScope: Option[Name]) = - sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName) + private def isRenamedSymbol(symNameInScope: Option[Name])(using Context) = + sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName) private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then From 2a2a1117a28fbc937ee815d5f86eec7c2a0c9971 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 18:08:33 +0200 Subject: [PATCH 60/97] Fix WUnused false positive in for --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 1 + tests/neg-custom-args/fatal-warnings/i15503i.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 04f993e4c805..c1d936cb1ca0 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -473,6 +473,7 @@ object CheckUnused: if ctx.settings.WunusedHas.explicits then explicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ExplicitParams).toList else diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index fbdf47dae17a..daf487a2fad0 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -300,4 +300,4 @@ package foo.test.i17117: val test = t1.test } - } \ No newline at end of file + } From fd7b9627855a112833967a37c1146fdda0251685 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 13:32:30 +0200 Subject: [PATCH 61/97] Do not register used symbol when position doesnt exist in wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 3 ++- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503i.scala | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index c1d936cb1ca0..4ee50c03ab85 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -358,7 +358,8 @@ object CheckUnused: usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived)) usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived)) usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived)) - name.map(n => usedInPosition += ((sym.sourcePos, n))) + if sym.sourcePos.exists then + name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ def addIgnoredUsage(sym: Symbol)(using Context): Unit = diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index 18aa6879eeba..912dbb456f3b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -60,7 +60,7 @@ class Revaluing(u: Int) { def f = u } // OK case class CaseyKasem(k: Int) // OK -case class CaseyAtTheBat(k: Int)(s: String) // error +case class CaseyAtTheBat(k: Int)(s: String) // ok trait Ignorance { def f(readResolve: Int) = answer // error diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index daf487a2fad0..436ee7ca0c0c 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -90,7 +90,7 @@ package foo.test.possibleclasses: k: Int, // OK private val y: Int // OK /* Kept as it can be taken from pattern */ )( - s: Int, // error /* But not these */ + s: Int, val t: Int, // OK private val z: Int // error ) @@ -131,7 +131,7 @@ package foo.test.possibleclasses.withvar: k: Int, // OK private var y: Int // OK /* Kept as it can be taken from pattern */ )( - s: Int, // error /* But not these */ + s: Int, var t: Int, // OK private var z: Int // error ) From 6356a39d7e0075125dd4c876a0470caed3ff8223 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 4 Apr 2023 17:20:02 +0200 Subject: [PATCH 62/97] Make CheckUnused run both after Typer and Inlining --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/transform/CheckUnused.scala | 92 ++++++++++++------- 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b03953afb37c..a488a2802ec2 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) :: // Check for unused elements + List(CheckUnused.PostTyper) :: // Check for unused 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 @@ -50,6 +50,7 @@ class Compiler { List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code + List(CheckUnused.PostInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 4ee50c03ab85..8d6aea8c97d1 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -33,22 +33,15 @@ import dotty.tools.dotc.core.StdNames.nme * Basically, it gathers definition/imports and their usage. If a * definition/imports does not have any usage, then it is reported. */ -class CheckUnused extends MiniPhase: - import CheckUnused.UnusedData - - /** - * The key used to retrieve the "unused entity" analysis metadata, - * from the compilation `Context` - */ - private val _key = Property.Key[UnusedData] +class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _key: Property.Key[CheckUnused.UnusedData]) extends MiniPhase: + import CheckUnused.* + import UnusedData.* private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = ctx.property(_key).foreach(f) ctx - private def getUnusedData(using Context): Option[UnusedData] = - ctx.property(_key) - override def phaseName: String = CheckUnused.phaseName + override def phaseName: String = CheckUnused.phaseNamePrefix + suffix override def description: String = CheckUnused.description @@ -60,13 +53,21 @@ class CheckUnused extends MiniPhase: override def prepareForUnit(tree: tpd.Tree)(using Context): Context = val data = UnusedData() + tree.getAttachment(_key).foreach(oldData => + data.unusedAggregate = oldData.unusedAggregate + ) val fresh = ctx.fresh.setProperty(_key, data) + tree.putAttachment(_key, data) fresh // ========== END + REPORTING ========== override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = - unusedDataApply(ud => reportUnused(ud.getUnused)) + unusedDataApply { ud => + aggregateUnused(ud, ud.getUnused) + if(phaseMode == PhaseMode.Report) then + ud.unusedAggregate.foreach(reportUnused) + } tree // ========== MiniPhase Prepare ========== @@ -252,31 +253,45 @@ class CheckUnused extends MiniPhase: private def traverseAnnotations(sym: Symbol)(using Context): Unit = sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) + private def aggregateUnused(data: UnusedData, res: UnusedData.UnusedResult)(using Context): Unit = + data.unusedAggregate match { + case None => + data.unusedAggregate = Some(res) + case Some(prevUnused) => + val intersection = res.warnings.filter(sym => prevUnused.warnings.contains(sym)) + data.unusedAggregate = Some(UnusedResult(intersection)) + } + + + /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = - import CheckUnused.WarnTypes res.warnings.foreach { s => s match - case (t, WarnTypes.Imports) => + case UnusedSymbol(t, _, WarnTypes.Imports) => report.warning(s"unused import", t) - case (t, WarnTypes.LocalDefs) => + case UnusedSymbol(t, _, WarnTypes.LocalDefs) => report.warning(s"unused local definition", t) - case (t, WarnTypes.ExplicitParams) => + case UnusedSymbol(t, _, WarnTypes.ExplicitParams) => report.warning(s"unused explicit parameter", t) - case (t, WarnTypes.ImplicitParams) => + case UnusedSymbol(t, _, WarnTypes.ImplicitParams) => report.warning(s"unused implicit parameter", t) - case (t, WarnTypes.PrivateMembers) => + case UnusedSymbol(t, _, WarnTypes.PrivateMembers) => report.warning(s"unused private member", t) - case (t, WarnTypes.PatVars) => + case UnusedSymbol(t, _, WarnTypes.PatVars) => report.warning(s"unused pattern variable", t) } end CheckUnused object CheckUnused: - val phaseName: String = "checkUnused" + val phaseNamePrefix: String = "checkUnused" val description: String = "check for unused elements" + enum PhaseMode: + case Aggregate + case Report + private enum WarnTypes: case Imports case LocalDefs @@ -285,6 +300,15 @@ object CheckUnused: case PrivateMembers case PatVars + /** + * The key used to retrieve the "unused entity" analysis metadata, + * from the compilation `Context` + */ + private val _key = Property.StickyKey[UnusedData] + + val PostTyper = new CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) + val PostInlining = new CheckUnused(PhaseMode.Report, "PostInlining", _key) + /** * A stateful class gathering the infos on : * - imports @@ -292,13 +316,14 @@ object CheckUnused: * - usage */ private class UnusedData: - import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} - import UnusedData.ScopeType + import UnusedData.* /** The current scope during the tree traversal */ var currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) + var unusedAggregate: Option[UnusedResult] = None + /* IMPORTS */ private val impInScope = MutStack(MutSet[tpd.Import]()) /** @@ -453,12 +478,13 @@ object CheckUnused: * * The given `List` is sorted by line and then column of the position */ + def getUnused(using Context): UnusedResult = popScope() val sortedImp = if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then - unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList + unusedImport.map(d => UnusedSymbol(d.srcPos, d.name, WarnTypes.Imports)).toList else Nil val sortedLocalDefs = @@ -467,7 +493,7 @@ object CheckUnused: .filterNot(d => d.symbol.usedDefContains) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.LocalDefs).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)).toList else Nil val sortedExplicitParams = @@ -476,7 +502,7 @@ object CheckUnused: .filterNot(d => d.symbol.usedDefContains) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.ExplicitParams).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ExplicitParams)).toList else Nil val sortedImplicitParams = @@ -484,7 +510,7 @@ object CheckUnused: implicitParamInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.ImplicitParams).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ImplicitParams)).toList else Nil val sortedPrivateDefs = @@ -492,7 +518,7 @@ object CheckUnused: privateDefInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.PrivateMembers).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)).toList else Nil val sortedPatVars = @@ -501,14 +527,14 @@ object CheckUnused: .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) - .map(d => d.namePos -> WarnTypes.PatVars).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PatVars)).toList else Nil val warnings = List(sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s => - val pos = s._1.sourcePos + val pos = s.pos.sourcePos (pos.line, pos.column) } - UnusedResult(warnings, Nil) + UnusedResult(warnings) end getUnused //============================ HELPERS ==================================== @@ -707,7 +733,11 @@ object CheckUnused: case _:tpd.Block => Local case _ => Other + case class UnusedSymbol(pos: SrcPos, name: Name, warnType: WarnTypes) /** A container for the results of the used elements analysis */ - case class UnusedResult(warnings: List[(dotty.tools.dotc.util.SrcPos, WarnTypes)], usedImports: List[(tpd.Import, untpd.ImportSelector)]) + case class UnusedResult(warnings: List[UnusedSymbol]) + object UnusedResult: + val Empty = UnusedResult(Nil) + end CheckUnused From 774c4e9e8e4a5b74472ab07032c74a7b4880e1d5 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 14:18:44 +0200 Subject: [PATCH 63/97] Fix instantation of CheckUnused phase --- compiler/src/dotty/tools/dotc/Compiler.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 ++++-- tests/neg-custom-args/fatal-warnings/i15503a.scala | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a488a2802ec2..15d4a39c511f 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(CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckUnused.PostTyper) :: // Check for unused 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 @@ -50,7 +50,7 @@ class Compiler { List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code - List(CheckUnused.PostInlining) :: // Check for unused elements + List(new CheckUnused.PostInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 8d6aea8c97d1..c1037ea59e75 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -52,6 +52,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + println(this) val data = UnusedData() tree.getAttachment(_key).foreach(oldData => data.unusedAggregate = oldData.unusedAggregate @@ -306,8 +307,9 @@ object CheckUnused: */ private val _key = Property.StickyKey[UnusedData] - val PostTyper = new CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) - val PostInlining = new CheckUnused(PhaseMode.Report, "PostInlining", _key) + class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) + + class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining", _key) /** * A stateful class gathering the infos on : diff --git a/tests/neg-custom-args/fatal-warnings/i15503a.scala b/tests/neg-custom-args/fatal-warnings/i15503a.scala index 868c488ddb84..cd7282490fc9 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503a.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503a.scala @@ -63,12 +63,12 @@ object FooTypeName: object InlineChecks: object InlineFoo: - import collection.mutable.Set // OK + import collection.mutable.Set // ok import collection.mutable.Map // error inline def getSet = Set(1) object InlinedBar: - import collection.mutable.Set // error + import collection.mutable.Set // ok import collection.mutable.Map // error val a = InlineFoo.getSet From a69b49f6d0b9c8fb76755056ef8c4bc297a7f78a Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 14:37:21 +0200 Subject: [PATCH 64/97] Remove unnecessary logging in CheckUnused phase --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index c1037ea59e75..1b879321184d 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -52,7 +52,6 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = - println(this) val data = UnusedData() tree.getAttachment(_key).foreach(oldData => data.unusedAggregate = oldData.unusedAggregate From 7966b5cd71b1d2d7eec4ad67880204062194f423 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 14:48:20 +0200 Subject: [PATCH 65/97] Add test cases for macro wunused --- .../fatal-warnings/i16876/Macro.scala | 23 +++++++++++++++++++ .../fatal-warnings/i16876/Test.scala | 11 +++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/neg-custom-args/fatal-warnings/i16876/Macro.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i16876/Test.scala diff --git a/tests/neg-custom-args/fatal-warnings/i16876/Macro.scala b/tests/neg-custom-args/fatal-warnings/i16876/Macro.scala new file mode 100644 index 000000000000..2823de1f72c5 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16876/Macro.scala @@ -0,0 +1,23 @@ +import scala.quoted.* + +def findMethodSymbol(using q: Quotes)(s: quotes.reflect.Symbol): quotes.reflect.Symbol = + if s.isDefDef then + s + else + findMethodSymbol(using q)(s.owner) +end findMethodSymbol + + +inline def adder: Int = ${ + adderImpl +} + +def adderImpl(using q: Quotes): Expr[Int] = + import quotes.reflect.* + + val inputs = findMethodSymbol(using q)(q.reflect.Symbol.spliceOwner).tree match + case DefDef(_, params, _, _) => + params.last match + case TermParamClause(valDefs) => + valDefs.map(vd => Ref(vd.symbol).asExprOf[Int]) + inputs.reduce((exp1, exp2) => '{ $exp1 + $exp2 }) \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i16876/Test.scala b/tests/neg-custom-args/fatal-warnings/i16876/Test.scala new file mode 100644 index 000000000000..d9229d31cd6d --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16876/Test.scala @@ -0,0 +1,11 @@ +// scalac: -Wunused:all + +object Foo { + private def myMethod(a: Int, b: Int, c: Int) = adder // ok + myMethod(1, 2, 3) + + private def myMethodFailing(a: Int, b: Int, c: Int) = a + 0 // error // error + myMethodFailing(1, 2, 3) +} + + From 644fee2feb1e9f3181dccab86faea7341fc09154 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 15:47:59 +0200 Subject: [PATCH 66/97] Apply review suggestions to WUnused PR --- .../tools/dotc/transform/CheckUnused.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 1b879321184d..c7e68079c0e8 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -25,7 +25,7 @@ import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.StdNames.nme - +import scala.math.Ordering /** * A compiler phase that checks for unused imports or definitions @@ -64,7 +64,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = unusedDataApply { ud => - aggregateUnused(ud, ud.getUnused) + finishAggregation(ud) if(phaseMode == PhaseMode.Report) then ud.unusedAggregate.foreach(reportUnused) } @@ -253,12 +253,13 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke private def traverseAnnotations(sym: Symbol)(using Context): Unit = sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) - private def aggregateUnused(data: UnusedData, res: UnusedData.UnusedResult)(using Context): Unit = + private def finishAggregation(data: UnusedData)(using Context): Unit = + val unusedInThisStage = data.getUnused data.unusedAggregate match { case None => - data.unusedAggregate = Some(res) + data.unusedAggregate = Some(unusedInThisStage) case Some(prevUnused) => - val intersection = res.warnings.filter(sym => prevUnused.warnings.contains(sym)) + val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) data.unusedAggregate = Some(UnusedResult(intersection)) } @@ -266,7 +267,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = - res.warnings.foreach { s => + res.warnings.toList.sortBy(_.pos.line)(using Ordering[Int]).foreach { s => s match case UnusedSymbol(t, _, WarnTypes.Imports) => report.warning(s"unused import", t) @@ -321,7 +322,7 @@ object CheckUnused: import UnusedData.* /** The current scope during the tree traversal */ - var currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) + val currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) var unusedAggregate: Option[UnusedResult] = None @@ -736,7 +737,7 @@ object CheckUnused: case class UnusedSymbol(pos: SrcPos, name: Name, warnType: WarnTypes) /** A container for the results of the used elements analysis */ - case class UnusedResult(warnings: List[UnusedSymbol]) + case class UnusedResult(warnings: Set[UnusedSymbol]) object UnusedResult: val Empty = UnusedResult(Nil) From e369d90b300d0d468f8d9595ed4756938df4cd16 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 15:55:29 +0200 Subject: [PATCH 67/97] Move finishAggregation to UnusedData class in CheckUnused --- .../tools/dotc/transform/CheckUnused.scala | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index c7e68079c0e8..468481e52441 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -64,7 +64,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = unusedDataApply { ud => - finishAggregation(ud) + ud.finishAggregation() if(phaseMode == PhaseMode.Report) then ud.unusedAggregate.foreach(reportUnused) } @@ -253,17 +253,6 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke private def traverseAnnotations(sym: Symbol)(using Context): Unit = sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) - private def finishAggregation(data: UnusedData)(using Context): Unit = - val unusedInThisStage = data.getUnused - data.unusedAggregate match { - case None => - data.unusedAggregate = Some(unusedInThisStage) - case Some(prevUnused) => - val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) - data.unusedAggregate = Some(UnusedResult(intersection)) - } - - /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = @@ -371,6 +360,17 @@ object CheckUnused: execInNewScope popScope() + def finishAggregation(using Context)(): Unit = + val unusedInThisStage = this.getUnused + this.unusedAggregate match { + case None => + this.unusedAggregate = Some(unusedInThisStage) + case Some(prevUnused) => + val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) + this.unusedAggregate = Some(UnusedResult(intersection)) + } + + /** * Register a found (used) symbol along with its name * @@ -536,7 +536,7 @@ object CheckUnused: val pos = s.pos.sourcePos (pos.line, pos.column) } - UnusedResult(warnings) + UnusedResult(warnings.toSet) end getUnused //============================ HELPERS ==================================== @@ -739,7 +739,7 @@ object CheckUnused: /** A container for the results of the used elements analysis */ case class UnusedResult(warnings: Set[UnusedSymbol]) object UnusedResult: - val Empty = UnusedResult(Nil) + val Empty = UnusedResult(Set.empty) end CheckUnused From 03dba67dd6d02aac7bcaec4a634241adca140646 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 10 Apr 2023 20:19:48 +0200 Subject: [PATCH 68/97] WIP: Disable WUnused for params of non-private defs --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 +++++++++++- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 468481e52441..e76b2abe95c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -118,6 +118,10 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = unusedDataApply{ ud => + if !tree.rawMods.is(Private) then + tree.termParamss.flatten.foreach { p => + ud.addIgnoredParam(p.symbol) + } import ud.registerTrivial tree.registerTrivial traverseAnnotations(tree.symbol) @@ -350,6 +354,8 @@ object CheckUnused: /** Trivial definitions, avoid registering params */ private val trivialDefs = MutSet[Symbol]() + private val paramsToSkip = MutSet[Symbol]() + /** * Push a new Scope of the given type, executes the given Unit and * pop it back to the original type. @@ -396,6 +402,10 @@ object CheckUnused: def removeIgnoredUsage(sym: Symbol)(using Context): Unit = doNotRegister --= sym.everySymbol + def addIgnoredParam(sym: Symbol)(using Context): Unit = + paramsToSkip += sym + + /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = @@ -411,7 +421,7 @@ object CheckUnused: if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then implicitParamInScope += memDef - else + else if !paramsToSkip.contains(memDef.symbol) then explicitParamInScope += memDef else if currScopeType.top == ScopeType.Local then localDefInScope += memDef diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index 912dbb456f3b..65354870f743 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -9,7 +9,7 @@ trait InterFace { } trait BadAPI extends InterFace { - def f(a: Int, + private def f(a: Int, b: String, // error c: Double): Int = { println(c) From 7017b8e4b1e90741468531dbc164695abb3686a5 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 15:32:56 +0200 Subject: [PATCH 69/97] Handle implicit params and adjust tests in WUnused --- .../tools/dotc/transform/CheckUnused.scala | 5 ++-- .../i15503-scala2/scala2-t11681.scala | 22 +++++++++--------- .../fatal-warnings/i15503e.scala | 18 ++++++++------- .../fatal-warnings/i15503f.scala | 17 +++++++------- .../fatal-warnings/i15503g.scala | 23 ++++++++++--------- .../fatal-warnings/i15503h.scala | 2 +- .../fatal-warnings/i15503i.scala | 10 +++++--- 7 files changed, 52 insertions(+), 45 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index e76b2abe95c6..df916fc76a3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -405,8 +405,6 @@ object CheckUnused: def addIgnoredParam(sym: Symbol)(using Context): Unit = paramsToSkip += sym - - /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then @@ -420,7 +418,8 @@ object CheckUnused: if memDef.isValidMemberDef then if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then - implicitParamInScope += memDef + if !paramsToSkip.contains(memDef.symbol) then + implicitParamInScope += memDef else if !paramsToSkip.contains(memDef.symbol) then explicitParamInScope += memDef else if currScopeType.top == ScopeType.Local then diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index 65354870f743..13d540dc2a5d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -33,7 +33,7 @@ trait BadAPI extends InterFace { override def equals(other: Any): Boolean = true // OK - def i(implicit s: String) = answer // error + def i(implicit s: String) = answer // ok /* def future(x: Int): Int = { @@ -63,7 +63,7 @@ case class CaseyKasem(k: Int) // OK case class CaseyAtTheBat(k: Int)(s: String) // ok trait Ignorance { - def f(readResolve: Int) = answer // error + def f(readResolve: Int) = answer // ok } class Reusing(u: Int) extends Unusing(u) // OK @@ -78,28 +78,28 @@ trait Unimplementation { } trait DumbStuff { - def f(implicit dummy: DummyImplicit) = answer // todo // error - def g(dummy: DummyImplicit) = answer // error + def f(implicit dummy: DummyImplicit) = answer // ok + def g(dummy: DummyImplicit) = answer // ok } trait Proofs { - def f[A, B](implicit ev: A =:= B) = answer // todo // error - def g[A, B](implicit ev: A <:< B) = answer // todo // error - def f2[A, B](ev: A =:= B) = answer // error - def g2[A, B](ev: A <:< B) = answer // error + def f[A, B](implicit ev: A =:= B) = answer // ok + def g[A, B](implicit ev: A <:< B) = answer // ok + def f2[A, B](ev: A =:= B) = answer // ok + def g2[A, B](ev: A <:< B) = answer // ok } trait Anonymous { - def f = (i: Int) => answer // error + def f = (i: Int) => answer // ok def f1 = (_: Int) => answer // OK def f2: Int => Int = _ + 1 // OK - def g = for (i <- List(1)) yield answer // error + def g = for (i <- List(1)) yield answer // ok } trait Context[A] trait Implicits { - def f[A](implicit ctx: Context[A]) = answer // error + def f[A](implicit ctx: Context[A]) = answer // ok def g[A: Context] = answer // OK } class Bound[A: Context] // OK diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 56aec702a39e..6d166aff7347 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -1,14 +1,16 @@ // scalac: -Wunused:explicits -/* This goes around the "trivial method" detection */ -val default_val = 1 +object Foo { + /* This goes around the "trivial method" detection */ + val default_val = 1 -def f1(a: Int) = a // OK -def f2(a: Int) = default_val // error -def f3(a: Int)(using Int) = a // OK -def f4(a: Int)(using Int) = default_val // error -def f6(a: Int)(using Int) = summon[Int] // error -def f7(a: Int)(using Int) = summon[Int] + a // OK + private def f1(a: Int) = a // OK + private def f2(a: Int) = default_val // error + private def f3(a: Int)(using Int) = a // OK + private def f4(a: Int)(using Int) = default_val // error + private def f6(a: Int)(using Int) = summon[Int] // error + private def f7(a: Int)(using Int) = summon[Int] + a // OK +} package scala2main.unused.args: object happyBirthday { diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index 67c595d74f40..f909272af732 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -3,11 +3,12 @@ /* This goes around the "trivial method" detection */ val default_int = 1 -def f1(a: Int) = a // OK -def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // OK -def f4(a: Int)(using Int) = default_int // OK -def f6(a: Int)(using Int) = summon[Int] // OK -def f7(a: Int)(using Int) = summon[Int] + a // OK -def f8(a: Int)(using foo: Int) = a // error - +object Xd { + private def f1(a: Int) = a // OK + private def f2(a: Int) = 1 // OK + private def f3(a: Int)(using Int) = a // OK + private def f4(a: Int)(using Int) = default_int // OK + private def f6(a: Int)(using Int) = summon[Int] // OK + private def f7(a: Int)(using Int) = summon[Int] + a // OK + private def f8(a: Int)(using foo: Int) = a // error +} diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index 8b3fd7561a4b..2185bfed711d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -1,18 +1,19 @@ // scalac: -Wunused:params /* This goes around the "trivial method" detection */ -val default_int = 1 +object Foo { + val default_int = 1 -def f1(a: Int) = a // OK -def f2(a: Int) = default_int // error -def f3(a: Int)(using Int) = a // OK -def f4(a: Int)(using Int) = default_int // error -def f6(a: Int)(using Int) = summon[Int] // error -def f7(a: Int)(using Int) = summon[Int] + a // OK - -/* --- Trivial method check --- */ -def g1(x: Int) = 1 // OK -def g2(x: Int) = ??? // OK + private def f1(a: Int) = a // OK + private def f2(a: Int) = default_int // error + private def f3(a: Int)(using Int) = a // OK + private def f4(a: Int)(using Int) = default_int // error + private def f6(a: Int)(using Int) = summon[Int] // error + private def f7(a: Int)(using Int) = summon[Int] + a // OK + /* --- Trivial method check --- */ + private def g1(x: Int) = 1 // OK + private def g2(x: Int) = ??? // OK +} package foo.test.i17101: type Test[A] = A diff --git a/tests/neg-custom-args/fatal-warnings/i15503h.scala b/tests/neg-custom-args/fatal-warnings/i15503h.scala index f8d1d6f2202f..3bab6cdbd098 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503h.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503h.scala @@ -7,7 +7,7 @@ class A { val b = 2 // OK private def c = 2 // error - def d(using x:Int): Int = b // error + def d(using x:Int): Int = b // ok def e(x: Int) = 1 // OK def f = val x = 1 // error diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 436ee7ca0c0c..e0f708e30f87 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -17,10 +17,10 @@ class A { private def c2 = 2 // OK def c3 = c2 - def d1(using x:Int): Int = default_int // error + def d1(using x:Int): Int = default_int // ok def d2(using x:Int): Int = x // OK - def e1(x: Int) = default_int // error + def e1(x: Int) = default_int // ok def e2(x: Int) = x // OK def f = val x = 1 // error @@ -44,7 +44,11 @@ package foo.test.scala.annotation: val default_int = 12 def a1(a: Int) = a // OK - def a2(a: Int) = default_int // error + def a2(a: Int) = default_int // ok + + private def a2_p(a: Int) = default_int // error + def a2_p_used = a2_p(3) + def a3(@unused a: Int) = default_int //OK def b1 = From b4e5cb7a7b862e386b9056c0c81cb275f4fd53fb Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 17:28:33 +0200 Subject: [PATCH 70/97] Fix tests for WUnused/disable for public defs --- .../fatal-warnings/i15503e.scala | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 6d166aff7347..57664cd08dcd 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -14,7 +14,7 @@ object Foo { package scala2main.unused.args: object happyBirthday { - def main(args: Array[String]): Unit = println("Hello World") // error + def main(args: Array[String]): Unit = println("Hello World") // ok } package scala2main: @@ -31,7 +31,7 @@ package scala3main: package foo.test.lambda.param: val default_val = 1 val a = (i: Int) => i // OK - val b = (i: Int) => default_val // error + val b = (i: Int) => default_val // OK val c = (_: Int) => default_val // OK package foo.test.trivial: @@ -39,19 +39,19 @@ package foo.test.trivial: class C { def answer: 42 = 42 object X - def g0(x: Int) = ??? // OK - def f0(x: Int) = () // OK - def f1(x: Int) = throw new RuntimeException // OK - def f2(x: Int) = 42 // OK - def f3(x: Int): Option[Int] = None // OK - def f4(x: Int) = classOf[Int] // OK - def f5(x: Int) = answer + 27 // OK - def f6(x: Int) = X // OK - def f7(x: Int) = Y // OK - def f8(x: Int): List[C] = Nil // OK - def f9(x: Int): List[Int] = List(1,2,3,4) // error - def foo:Int = 32 // OK - def f77(x: Int) = foo // error + private def g0(x: Int) = ??? // OK + private def f0(x: Int) = () // OK + private def f1(x: Int) = throw new RuntimeException // OK + private def f2(x: Int) = 42 // OK + private def f3(x: Int): Option[Int] = None // OK + private def f4(x: Int) = classOf[Int] // OK + private def f5(x: Int) = answer + 27 // OK + private def f6(x: Int) = X // OK + private def f7(x: Int) = Y // OK + private def f8(x: Int): List[C] = Nil // OK + private def f9(x: Int): List[Int] = List(1,2,3,4) // error + private def foo:Int = 32 // OK + private def f77(x: Int) = foo // error } object Y From 2af117f75ad95b29407b9545fdd9b345e9a48fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 14 Apr 2023 18:27:58 +0200 Subject: [PATCH 71/97] Add missing test for Wunused --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index e0f708e30f87..3b653bfadb0a 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -292,6 +292,17 @@ package foo.test.i17156: import b.Xd trait Z derives Xd + +package foo.test.i17175: + val continue = true + def foo = + for { + i <- 1.until(10) // OK + if continue + } { + println(i) + } + package foo.test.i17117: package example { object test1 { From 84458c712a5757224280d5c5bbdb55841cf1c370 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 14 Apr 2023 12:58:37 +0200 Subject: [PATCH 72/97] Bring in #17263 to fix the tests. --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503i.scala | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index df916fc76a3b..ce05e6c125de 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -118,7 +118,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = unusedDataApply{ ud => - if !tree.rawMods.is(Private) then + if !tree.symbol.is(Private) then tree.termParamss.flatten.foreach { p => ud.addIgnoredParam(p.symbol) } diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 3b653bfadb0a..fefead7f01a3 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -46,9 +46,6 @@ package foo.test.scala.annotation: def a1(a: Int) = a // OK def a2(a: Int) = default_int // ok - private def a2_p(a: Int) = default_int // error - def a2_p_used = a2_p(3) - def a3(@unused a: Int) = default_int //OK def b1 = From 3156fe97c70cb931d60e8299117b4c9eff093d26 Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Fri, 17 Mar 2023 14:59:39 +0100 Subject: [PATCH 73/97] Check the status of coursier download in CoursierScalaTests.scala This should provide more insight on why #17119 happens. --- .../tools/coursier/CoursierScalaTests.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 979fea0684b2..944bf1957d43 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -148,11 +148,11 @@ class CoursierScalaTests: object CoursierScalaTests: - def execCmd(command: String, options: String*): List[String] = + def execCmd(command: String, options: String*): (Int, List[String]) = val cmd = (command :: options.toList).toSeq.mkString(" ") val out = new ListBuffer[String] - cmd.!(ProcessLogger(out += _, out += _)) - out.toList + val code = cmd.!(ProcessLogger(out += _, out += _)) + (code, out.toList) def csScalaCmd(options: String*): List[String] = csCmd("dotty.tools.MainGenericRunner", options*) @@ -166,10 +166,16 @@ object CoursierScalaTests: case Nil => args case _ => "--" +: args val newJOpts = jOpts.map(s => s"--java-opt ${s.stripPrefix("-J")}").mkString(" ") - execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" $newJOpts --main-class "$entry" --property "scala.usejavacp=true"""" +: newOptions)*) + execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" $newJOpts --main-class "$entry" --property "scala.usejavacp=true"""" +: newOptions)*)._2 /** Get coursier script */ @BeforeClass def setup(): Unit = - val ver = execCmd("uname").head.replace('L', 'l').replace('D', 'd') - execCmd("curl", s"-fLo cs https://git.io/coursier-cli-$ver") #&& execCmd("chmod", "+x cs") + val ver = execCmd("uname")._2.head.replace('L', 'l').replace('D', 'd') + def runAndCheckCmd(cmd: String, options: String*): Unit = + val (code, out) = execCmd(cmd, options*) + if code != 0 then + fail(s"Failed to run $cmd ${options.mkString(" ")}, exit code: $code, output: ${out.mkString("\n")}") + + runAndCheckCmd("curl", s"-fLo cs https://git.io/coursier-cli-$ver") + runAndCheckCmd("chmod", "+x cs") From 22e6ffe7529bd888275218c02b377207e9fc2c0c 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 74/97] Add changelog for 3.3.0-RC4 --- 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 5990252b77128f9599d7b21c2444d3cbaa6fbb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 17 Apr 2023 14:46:55 +0200 Subject: [PATCH 75/97] Release 3.3.0-RC4 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 4360add9578a..dddddf20c1ce 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.2.2" - val baseVersion = "3.3.0-RC3" + val baseVersion = "3.3.0-RC4" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.0-RC2" + val previousDottyVersion = "3.3.0-RC3" object CompatMode { final val BinaryCompatible = 0 From 40502e03042577d10283195ff4c3c3e72c4bcfaf Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Mon, 3 Apr 2023 10:55:26 +0200 Subject: [PATCH 76/97] Drop network tests in requests community-build --- community-build/community-projects/requests-scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/requests-scala b/community-build/community-projects/requests-scala index 23b4895710f1..8e4a40588491 160000 --- a/community-build/community-projects/requests-scala +++ b/community-build/community-projects/requests-scala @@ -1 +1 @@ -Subproject commit 23b4895710f17bf892563b28755b225c8be7f7e3 +Subproject commit 8e4a40588491608aa40099f79c881d54a5094e75 From 72e5dd28c91ca1f4421aa8c244d239bfbb8969c0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 25 Apr 2023 14:04:49 +0200 Subject: [PATCH 77/97] Fix compiler crash in WUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 3 ++- tests/neg-custom-args/fatal-warnings/i17335.scala | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i17335.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ce05e6c125de..32afe9c8a1e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -47,7 +47,8 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def isRunnable(using Context): Boolean = ctx.settings.Wunused.value.nonEmpty && - !ctx.isJava + !ctx.isJava && + super.isRunnable // ========== SETUP ============ diff --git a/tests/neg-custom-args/fatal-warnings/i17335.scala b/tests/neg-custom-args/fatal-warnings/i17335.scala new file mode 100644 index 000000000000..6629e2f151c9 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17335.scala @@ -0,0 +1,4 @@ +// scalac: -Wunused:all + +def aMethod() = + doStuff { (x) => x } // error From 46d9c07c0b4aca30f87371043702c624730bdf08 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 25 Apr 2023 14:12:56 +0200 Subject: [PATCH 78/97] Change the order of checks --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 32afe9c8a1e7..371df57045b4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -46,9 +46,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def description: String = CheckUnused.description override def isRunnable(using Context): Boolean = + super.isRunnable && ctx.settings.Wunused.value.nonEmpty && - !ctx.isJava && - super.isRunnable + !ctx.isJava // ========== SETUP ============ From 716b8670210af194452c0009009d7b31ebd25158 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 19 Apr 2023 15:50:29 +0200 Subject: [PATCH 79/97] Wunused: Check if symbol exists before isValidMemberDef check closes lampepfl#17309 --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ce05e6c125de..b3083223c555 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -681,7 +681,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) || - (if sym.exists then sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists) else false) + (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) end extension @@ -708,7 +708,11 @@ object CheckUnused: extension (memDef: tpd.MemberDef) private def isValidMemberDef(using Context): Boolean = - !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard && !memDef.symbol.owner.is(Extension) + memDef.symbol.exists + && !memDef.symbol.isUnusedAnnot + && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) + && !memDef.name.isWildcard + && !memDef.symbol.owner.is(ExtensionMethod) private def isValidParam(using Context): Boolean = val sym = memDef.symbol From 12cd96e1de30c8f59183feabe25fe688a95a9441 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 21 Apr 2023 01:35:46 +0200 Subject: [PATCH 80/97] Wunused: Include import selector bounds in unused checks closes lampepfl#17314 --- .../tools/dotc/transform/CheckUnused.scala | 34 ++++++++++++------- .../fatal-warnings/i17314b.scala | 14 ++++++++ tests/pos-special/fatal-warnings/i17314.scala | 33 ++++++++++++++++++ .../pos-special/fatal-warnings/i17314a.scala | 12 +++++++ 4 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i17314b.scala create mode 100644 tests/pos-special/fatal-warnings/i17314.scala create mode 100644 tests/pos-special/fatal-warnings/i17314a.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ce05e6c125de..baef470c8e88 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -202,8 +202,11 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def traverse(tree: tpd.Tree)(using Context): Unit = val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx tree match - case imp:tpd.Import => + case imp: tpd.Import => unusedDataApply(_.registerImport(imp)) + imp.selectors.filter(_.isGiven).map(_.bound).collect { + case untpd.TypedSplice(tree1) => tree1 + }.foreach(traverse(_)(using newCtx)) traverseChildren(tree)(using newCtx) case ident: Ident => prepareForIdent(ident) @@ -449,13 +452,12 @@ object CheckUnused: val used = usedInScope.pop().toSet // used imports in this scope val imports = impInScope.pop() - val kept = used.filterNot { t => - val (sym, isAccessible, optName, isDerived) = t + val kept = used.filterNot { (sym, isAccessible, optName, isDerived) => // keep the symbol for outer scope, if it matches **no** import // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None - val exists = imports.exists { imp => + val matchedExplicitImport = imports.exists { imp => sym.isInImport(imp, isAccessible, optName, isDerived) match case None => false case optSel@Some(sel) if sel.isWildcard => @@ -466,11 +468,11 @@ object CheckUnused: unusedImport -= sel true } - if !exists && selWildCard.isDefined then + if !matchedExplicitImport && selWildCard.isDefined then unusedImport -= selWildCard.get true // a matching import exists so the symbol won't be kept for outer scope else - exists + matchedExplicitImport } // if there's an outer scope @@ -610,12 +612,17 @@ object CheckUnused: * return true */ private def shouldSelectorBeReported(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = - if ctx.settings.WunusedHas.strictNoImplicitWarn then + ctx.settings.WunusedHas.strictNoImplicitWarn && ( 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)) - else - false + ) + + extension (tree: ImportSelector) + def boundTpe: Type = tree.bound match { + case untpd.TypedSplice(tree1) => tree1.tpe + case _ => NoType + } extension (sym: Symbol) /** is accessible without import in current context */ @@ -628,7 +635,7 @@ object CheckUnused: && c.owner.thisType.member(sym.name).alternatives.contains(sym) } - /** Given an import and accessibility, return an option of selector that match import<->symbol */ + /** Given an import and accessibility, return selector that matches import<->symbol */ private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) @@ -641,9 +648,12 @@ object CheckUnused: def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None - def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) + def givenSelector = if sym.is(Given) || sym.is(Implicit) + then sels.filter(sel => sel.isGiven && !sel.bound.isEmpty).find(sel => sel.boundTpe =:= sym.info) + 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(wildcard) // selector with name or wildcard (or given) + selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard) // selector with name or wildcard (or given) else None diff --git a/tests/neg-custom-args/fatal-warnings/i17314b.scala b/tests/neg-custom-args/fatal-warnings/i17314b.scala new file mode 100644 index 000000000000..384767765cf4 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17314b.scala @@ -0,0 +1,14 @@ +// scalac: -Wunused:all + +package foo: + class Foo[T] + given Foo[Int] = new Foo[Int] + + +package bar: + import foo.{given foo.Foo[Int]} // error + import foo.Foo + + given Foo[Int] = ??? + + val repro: Foo[Int] = summon[Foo[Int]] diff --git a/tests/pos-special/fatal-warnings/i17314.scala b/tests/pos-special/fatal-warnings/i17314.scala new file mode 100644 index 000000000000..23f988741bed --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17314.scala @@ -0,0 +1,33 @@ +// scalac: "-Wunused:all" + +import java.net.URI + +object circelike { + import scala.compiletime.summonInline + import scala.deriving.Mirror + + type Codec[T] + type Configuration + trait ConfiguredCodec[T] + object ConfiguredCodec: + inline final def derived[A](using conf: Configuration)(using + inline mirror: Mirror.Of[A] + ): ConfiguredCodec[A] = + new ConfiguredCodec[A]: + val codec = summonInline[Codec[URI]] // simplification +} + +object foo { + import circelike.{Codec, Configuration} + + given Configuration = ??? + given Codec[URI] = ??? +} + +object bar { + import circelike.Codec + import circelike.{Configuration, ConfiguredCodec} + import foo.{given Configuration, given Codec[URI]} + + case class Operator(url: URI) derives ConfiguredCodec +} diff --git a/tests/pos-special/fatal-warnings/i17314a.scala b/tests/pos-special/fatal-warnings/i17314a.scala new file mode 100644 index 000000000000..468b956fb04c --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17314a.scala @@ -0,0 +1,12 @@ +// scalac: -Wunused:all + +package foo: + class Foo[T] + given Foo[Int] = new Foo[Int] + + +package bar: + import foo.{given foo.Foo[Int]} + import foo.Foo + + val repro: Foo[Int] = summon[Foo[Int]] From 74b0aa46c7e5e4fc1b2a3828cc6aa5ac0acd1f47 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Fri, 3 Feb 2023 20:43:05 -0500 Subject: [PATCH 81/97] Remove experimental from Mirror#fromProductTyped --- library/src/scala/deriving/Mirror.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/library/src/scala/deriving/Mirror.scala b/library/src/scala/deriving/Mirror.scala index 5de219dfe5c4..57453a516567 100644 --- a/library/src/scala/deriving/Mirror.scala +++ b/library/src/scala/deriving/Mirror.scala @@ -52,7 +52,6 @@ object Mirror { extension [T](p: ProductOf[T]) /** Create a new instance of type `T` with elements taken from product `a`. */ - @annotation.experimental def fromProductTyped[A <: scala.Product, Elems <: p.MirroredElemTypes](a: A)(using m: ProductOf[A] { type MirroredElemTypes = Elems }): T = p.fromProduct(a) From a55322d74c9fb18cb6e0562018dacee4623a9731 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 4 Feb 2023 08:42:54 -0500 Subject: [PATCH 82/97] Update experimental definitions list --- .../tasty-inspector/stdlibExperimentalDefinitions.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 30e7c5af6c2a..062fa25e0ca5 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -58,10 +58,6 @@ val experimentalDefinitionInLibrary = Set( //// New feature: into "scala.annotation.allowConversions", - //// New APIs: Mirror - // Can be stabilized in 3.3.0 or later. - "scala.deriving.Mirror$.fromProductTyped", // This API is a bit convoluted. We may need some more feedback before we can stabilize it. - //// New feature: Macro annotations "scala.annotation.MacroAnnotation", From 909b56c0d4b40901053af1dad3858a882286c855 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 83/97] Add changelog for 3.3.0-RC5 --- 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 597144e8805fc21e2f0a938f6701326df7aafe30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 27 Apr 2023 10:17:23 +0200 Subject: [PATCH 84/97] Release 3.3.0-RC5 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index dddddf20c1ce..03d482a83407 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.2.2" - val baseVersion = "3.3.0-RC4" + val baseVersion = "3.3.0-RC5" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.0-RC3" + val previousDottyVersion = "3.3.0-RC4" object CompatMode { final val BinaryCompatible = 0 From 3b9b83d9c2f0ba31bda219756729c45b7052f45b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 3 Feb 2023 13:55:56 +0000 Subject: [PATCH 85/97] Patmat: Use less type variables in prefix inference In code like: class Outer: sealed trait Foo case class Bar() extends Foo def mat(foo: Foo) = foo match case Bar() => When in the course of decomposing the scrutinee's type, which is `Outer.this.Foo`, we're trying to instantiate subclass `Outer.this.Bar`, the `Outer.this` is fixed - it needn't be inferred, via type variables and type bounds. Cutting down on type variables, particularly when GADT symbols are also present, can really speed up the operation, including making code that used to hang forever compile speedily. --- .../src/dotty/tools/dotc/core/TypeOps.scala | 53 +++++++++++++------ tests/patmat/i12408.check | 2 +- tests/pos/i16785.scala | 11 ++++ 3 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 tests/pos/i16785.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index d9da11c561e8..c91412988e82 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc package core -import Contexts._, Types._, Symbols._, Names._, Flags._ +import Contexts._, Types._, Symbols._, Names._, NameKinds.*, Flags._ import SymDenotations._ import util.Spans._ import util.Stats @@ -839,24 +839,51 @@ object TypeOps: } } - // Prefix inference, replace `p.C.this.Child` with `X.Child` where `X <: p.C` - // Note: we need to strip ThisType in `p` recursively. + /** Gather GADT symbols and `ThisType`s found in `tp2`, ie. the scrutinee. */ + object TraverseTp2 extends TypeTraverser: + val thisTypes = util.HashSet[ThisType]() + val gadtSyms = new mutable.ListBuffer[Symbol] + + def traverse(tp: Type) = { + val tpd = tp.dealias + if tpd ne tp then traverse(tpd) + else tp match + case tp: ThisType if !tp.tref.symbol.isStaticOwner && !thisTypes.contains(tp) => + thisTypes += tp + traverseChildren(tp.tref) + case tp: TypeRef if tp.symbol.isAbstractOrParamType => + gadtSyms += tp.symbol + traverseChildren(tp) + case _ => + traverseChildren(tp) + } + TraverseTp2.traverse(tp2) + val thisTypes = TraverseTp2.thisTypes + val gadtSyms = TraverseTp2.gadtSyms.toList + + // Prefix inference, given `p.C.this.Child`: + // 1. return it as is, if `C.this` is found in `tp`, i.e. the scrutinee; or + // 2. replace it with `X.Child` where `X <: p.C`, stripping ThisType in `p` recursively. // - // See tests/patmat/i3938.scala + // See tests/patmat/i3938.scala, tests/pos/i15029.more.scala, tests/pos/i16785.scala class InferPrefixMap extends TypeMap { var prefixTVar: Type | Null = null def apply(tp: Type): Type = tp match { - case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner => + case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => val symbol = tref.symbol - if (symbol.is(Module)) + if thisTypes.contains(tp) then + prefixTVar = tp // e.g. tests/pos/i16785.scala, keep Outer.this + prefixTVar.uncheckedNN + else if symbol.is(Module) then TermRef(this(tref.prefix), symbol.sourceModule) else if (prefixTVar != null) this(tref) else { prefixTVar = WildcardType // prevent recursive call from assigning it - val tvars = tref.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + // e.g. tests/pos/i15029.more.scala, create a TypeVar for `Instances`' B, so we can disregard `Ints` + val tvars = tref.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) } val tref2 = this(tref.applyIfParameterized(tvars)) - prefixTVar = newTypeVar(TypeBounds.upper(tref2)) + prefixTVar = newTypeVar(TypeBounds.upper(tref2), DepParamName.fresh(tref.name)) prefixTVar.uncheckedNN } case tp => mapOver(tp) @@ -864,15 +891,11 @@ object TypeOps: } val inferThisMap = new InferPrefixMap - val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) } val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars) - val getAbstractSymbols = new TypeAccumulator[List[Symbol]]: - def apply(xs: List[Symbol], tp: Type) = tp.dealias match - case tp: TypeRef if tp.symbol.exists && !tp.symbol.isClass => foldOver(tp.symbol :: xs, tp) - case tp => foldOver(xs, tp) - val syms2 = getAbstractSymbols(Nil, tp2).reverse - if syms2.nonEmpty then ctx.gadtState.addToConstraint(syms2) + if gadtSyms.nonEmpty then + ctx.gadtState.addToConstraint(gadtSyms) // If parent contains a reference to an abstract type, then we should // refine subtype checking to eliminate abstract types according to diff --git a/tests/patmat/i12408.check b/tests/patmat/i12408.check index ada7b8c21fa8..60acc2cba84e 100644 --- a/tests/patmat/i12408.check +++ b/tests/patmat/i12408.check @@ -1,2 +1,2 @@ -13: Pattern Match Exhaustivity: X[] & (X.this : X[T]).A(_), X[] & (X.this : X[T]).C(_) +13: Pattern Match Exhaustivity: A(_), C(_) 21: Pattern Match diff --git a/tests/pos/i16785.scala b/tests/pos/i16785.scala new file mode 100644 index 000000000000..1cfabf5a4312 --- /dev/null +++ b/tests/pos/i16785.scala @@ -0,0 +1,11 @@ +class VarImpl[Lbl, A] + +class Outer[|*|[_, _], Lbl1]: + type Var[A1] = VarImpl[Lbl1, A1] + + sealed trait Foo[G] + case class Bar[T, U]() + extends Foo[Var[T] |*| Var[U]] + + def go[X](scr: Foo[Var[X]]): Unit = scr match // was: compile hang + case Bar() => () From 9c1cdc882bbba6b302663852fc36252e395449b5 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Fri, 28 Apr 2023 14:40:34 +0200 Subject: [PATCH 86/97] Fix #17187: allow patches with same span --- .../dotty/tools/dotc/parsing/Parsers.scala | 9 ++-- .../dotty/tools/dotc/rewrites/Rewrites.scala | 5 +-- .../dotty/tools/dotc/CompilationTests.scala | 1 + tests/rewrites/i12340.check | 9 ++++ tests/rewrites/i12340.scala | 7 +++ tests/rewrites/i17187.check | 44 +++++++++++++++++++ tests/rewrites/i17187.scala | 33 ++++++++++++++ 7 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 tests/rewrites/i17187.check create mode 100644 tests/rewrites/i17187.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ff5e95f3aa03..fbf31cc8cbbd 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -704,7 +704,11 @@ object Parsers { val t = enclosed(INDENT, body) if needsBraces(t) then patch(source, Span(startOpening, endOpening), " {") - patch(source, Span(closingOffset(source.nextLine(in.lastOffset))), indentWidth.toPrefix ++ "}\n") + val next = in.next + def closedByEndMarker = + next.token == END && (next.offset - next.lineOffset) == indentWidth.toPrefix.size + if closedByEndMarker then patch(source, Span(next.offset), "} // ") + else patch(source, Span(closingOffset(source.nextLine(in.lastOffset))), indentWidth.toPrefix ++ "}\n") t end indentedToBraces @@ -1411,9 +1415,6 @@ object Parsers { val start = in.skipToken() if stats.isEmpty || !matchesAndSetEnd(stats.last) then syntaxError(em"misaligned end marker", Span(start, in.lastCharOffset)) - else if overlapsPatch(source, Span(start, start)) then - patch(source, Span(start, start), "") - patch(source, Span(start, in.lastCharOffset), s"} // end $endName") in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion in.nextToken() end checkEndMarker diff --git a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala index 96e88e5c68ae..f2dfac88d464 100644 --- a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala +++ b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala @@ -23,10 +23,7 @@ object Rewrites { private[Rewrites] val pbuf = new mutable.ListBuffer[Patch]() def addPatch(span: Span, replacement: String): Unit = - pbuf.indexWhere(p => p.span.start == span.start && p.span.end == span.end) match { - case i if i >= 0 => pbuf.update(i, Patch(span, replacement)) - case _ => pbuf += Patch(span, replacement) - } + pbuf += Patch(span, replacement) def apply(cs: Array[Char]): Array[Char] = { val delta = pbuf.map(_.delta).sum diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index b8b38cce92e4..191bfa0a1e93 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -83,6 +83,7 @@ class CompilationTests { compileFile("tests/rewrites/i9632.scala", defaultOptions.and("-indent", "-rewrite")), compileFile("tests/rewrites/i11895.scala", defaultOptions.and("-indent", "-rewrite")), compileFile("tests/rewrites/i12340.scala", unindentOptions.and("-rewrite")), + compileFile("tests/rewrites/i17187.scala", unindentOptions.and("-rewrite")), ).checkRewrites() } diff --git a/tests/rewrites/i12340.check b/tests/rewrites/i12340.check index c6cb9af8bb57..3ee2867f3e62 100644 --- a/tests/rewrites/i12340.check +++ b/tests/rewrites/i12340.check @@ -3,6 +3,15 @@ class C { def f = 42 } // end C +class A { + class B { + class C { + def foo = 42 + } + + } // end B +} + def f(i: Int) = { if i < 42 then println(i) diff --git a/tests/rewrites/i12340.scala b/tests/rewrites/i12340.scala index bf907ef9f276..10fcdd256f5b 100644 --- a/tests/rewrites/i12340.scala +++ b/tests/rewrites/i12340.scala @@ -3,6 +3,13 @@ class C: def f = 42 end C +class A: + class B: + class C: + def foo = 42 + + end B + def f(i: Int) = if i < 42 then println(i) diff --git a/tests/rewrites/i17187.check b/tests/rewrites/i17187.check new file mode 100644 index 000000000000..1e6a07c79738 --- /dev/null +++ b/tests/rewrites/i17187.check @@ -0,0 +1,44 @@ + +object A { + object B { + def a = 2 + } +} + +def m1 = { + def b = { + def c = 2 + } +} + +def m2 = + if true then { + val x = 3 + if (false) + x + else { + val y = 4 + y + } + } + +def m3 = + try { + val n2 = 21 + val n1 = 4 + n2 / n1 + } + catch { + case _ => 4 + } + +def m4 = { + val n2 = 21 + try { + val n1 = 4 + n2 / n1 + } + catch { + case _ => 4 + } +} diff --git a/tests/rewrites/i17187.scala b/tests/rewrites/i17187.scala new file mode 100644 index 000000000000..e6a55e00b39a --- /dev/null +++ b/tests/rewrites/i17187.scala @@ -0,0 +1,33 @@ + +object A: + object B: + def a = 2 + +def m1 = + def b = + def c = 2 + +def m2 = + if true then + val x = 3 + if (false) + x + else + val y = 4 + y + +def m3 = + try + val n2 = 21 + val n1 = 4 + n2 / n1 + catch + case _ => 4 + +def m4 = + val n2 = 21 + try + val n1 = 4 + n2 / n1 + catch + case _ => 4 From c1028a225f6a1e40c8eb6c94b8fa530fee6b8ae5 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Mon, 1 May 2023 09:43:44 +0200 Subject: [PATCH 87/97] Revert exact match in overlaps As suggested by @som-snytt in https://github.com/lampepfl/dotty/pull/17366#pullrequestreview-1406509043 --- compiler/src/dotty/tools/dotc/util/Spans.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/util/Spans.scala b/compiler/src/dotty/tools/dotc/util/Spans.scala index ba537e9aec01..e1487408f36b 100644 --- a/compiler/src/dotty/tools/dotc/util/Spans.scala +++ b/compiler/src/dotty/tools/dotc/util/Spans.scala @@ -86,7 +86,6 @@ object Spans { || containsInner(this, that.end) || containsInner(that, this.start) || containsInner(that, this.end) - || this.start == that.start && this.end == that.end // exact match in one point ) } From 9a1e7cb5b23c807dfe5918a818cd46ac277ae8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Wed, 10 May 2023 13:41:35 +0200 Subject: [PATCH 88/97] Raise a warning instead of an error for a type ascription on a pattern other than a variable or a number literal. This partially reverts the changes from https://github.com/lampepfl/dotty/pull/16150. This change is motivated by not breaking source compatibility for a number of projects in the Open Community Build. --- .../dotty/tools/dotc/parsing/Parsers.scala | 10 +-- docs/_docs/internals/syntax.md | 5 +- docs/_docs/reference/syntax.md | 5 +- .../fatal-warnings}/i10994.scala | 0 .../fatal-warnings}/i15893.scala | 6 +- tests/neg/t5702-neg-bad-and-wild.check | 10 ++- tests/pending/run/i15893.scala | 6 +- tests/pos/i10994.scala | 2 + tests/pos/i15893.scala | 61 +++++++++++++++++++ 9 files changed, 83 insertions(+), 22 deletions(-) rename tests/{neg => neg-custom-args/fatal-warnings}/i10994.scala (100%) rename tests/{neg => neg-custom-args/fatal-warnings}/i15893.scala (89%) create mode 100644 tests/pos/i10994.scala create mode 100644 tests/pos/i15893.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ff5e95f3aa03..c42b302912a9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2821,14 +2821,14 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= PatVar Ascription - * | [‘-’] integerLiteral Ascription - * | [‘-’] floatingPointLiteral Ascription - * | Pattern2 + /** Pattern1 ::= Pattern2 [Ascription] */ def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() - if (isVarPattern(p) || p.isInstanceOf[Number]) && in.isColon then + if in.isColon then + val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] + if !isVariableOrNumber then + warning(em"Only variable and number literal patterns can have type ascriptions") in.nextToken() ascription(p, location) else p diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 76664569bb17..cf6a6d053566 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -319,10 +319,7 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) -Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe)) - | [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe) - | [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe) - | Pattern2 +Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 7e4b81b1ef5a..8d110f685f9d 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -312,10 +312,7 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= PatVar ‘:’ RefinedType - | [‘-’] integerLiteral ‘:’ RefinedType - | [‘-’] floatingPointLiteral ‘:’ RefinedType - | Pattern2 +Pattern1 ::= Pattern2 [‘:’ RefinedType] Pattern2 ::= [id ‘@’] InfixPattern [‘*’] InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar diff --git a/tests/neg/i10994.scala b/tests/neg-custom-args/fatal-warnings/i10994.scala similarity index 100% rename from tests/neg/i10994.scala rename to tests/neg-custom-args/fatal-warnings/i10994.scala diff --git a/tests/neg/i15893.scala b/tests/neg-custom-args/fatal-warnings/i15893.scala similarity index 89% rename from tests/neg/i15893.scala rename to tests/neg-custom-args/fatal-warnings/i15893.scala index 997c51179099..f23e6150106a 100644 --- a/tests/neg/i15893.scala +++ b/tests/neg-custom-args/fatal-warnings/i15893.scala @@ -22,7 +22,7 @@ transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n m case Succ(Zero()) => Succ(Zero()) case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match case Zero(): Zero => Zero() // error case Succ(Zero()): Succ[Zero] => Succ(Zero()) // error case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // error @@ -57,5 +57,5 @@ inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInline println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected -// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected -// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected + println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 731195411069..36ac71b2e1e7 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -10,10 +10,10 @@ | pattern expected | | longer explanation available when compiling with `-explain` --- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------- +-- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:23 --------------------------------------------------- 13 | case List(1, _*3:) => // error // error - | ^ - | ')' expected, but ':' found + | ^ + | an identifier expected, but ')' found -- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:15:18 --------------------------------------------------- 15 | case List(x*, 1) => // error: pattern expected | ^ @@ -56,6 +56,10 @@ | 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 + | ^ + | Only variable and number literal patterns can have type ascriptions -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:22:20 --------------------------------------------------------------- 22 | val K(x @ _*) = k | ^ diff --git a/tests/pending/run/i15893.scala b/tests/pending/run/i15893.scala index dedec2138f2a..d9cd2822e971 100644 --- a/tests/pending/run/i15893.scala +++ b/tests/pending/run/i15893.scala @@ -24,7 +24,7 @@ transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n m case Succ(Zero()) => Succ(Zero()) case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) */ -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match case Zero(): Zero => Zero() case Succ(Zero()): Succ[Zero] => Succ(Zero()) case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) @@ -61,5 +61,5 @@ inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInline println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected */ println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected -// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected -// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected +// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected +// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected diff --git a/tests/pos/i10994.scala b/tests/pos/i10994.scala new file mode 100644 index 000000000000..99ae647466b1 --- /dev/null +++ b/tests/pos/i10994.scala @@ -0,0 +1,2 @@ +def foo = true match + case (b: Boolean): Boolean => () diff --git a/tests/pos/i15893.scala b/tests/pos/i15893.scala new file mode 100644 index 000000000000..af6e7ae38ad2 --- /dev/null +++ b/tests/pos/i15893.scala @@ -0,0 +1,61 @@ +sealed trait NatT +case class Zero() extends NatT +case class Succ[+N <: NatT](n: N) extends NatT + +type Mod2[N <: NatT] <: NatT = N match + case Zero => Zero + case Succ[Zero] => Succ[Zero] + case Succ[Succ[predPredN]] => Mod2[predPredN] + +def mod2(n: NatT): NatT = n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => mod2(predPredN) + +inline def inlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => inlineMod2(predPredN) + +transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) + +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // warning + +inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN) // warning + +transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN) // warning + +def foo(n: NatT): NatT = mod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +@main def main(): Unit = + println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected + println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected + println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected From 16d68f6238ef668b53c5e762622ae87f0d1cd002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Thu, 11 May 2023 13:26:45 +0200 Subject: [PATCH 89/97] * Preserve the more restrictive syntax for typed patterns in the language specification * Make the parser's warning a migration warning --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 15 +++++++++++++-- .../test/dotty/tools/dotc/CompilationTests.scala | 1 + docs/_docs/internals/syntax.md | 5 ++++- docs/_docs/reference/syntax.md | 5 ++++- tests/neg-custom-args/i10994.check | 7 +++++++ tests/neg-custom-args/i10994.scala | 2 ++ tests/neg/t5702-neg-bad-and-wild.check | 5 ++++- tests/pos/i10994.scala | 2 +- 8 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 tests/neg-custom-args/i10994.check create mode 100644 tests/neg-custom-args/i10994.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c42b302912a9..605afc07cc72 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2821,14 +2821,25 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= Pattern2 [Ascription] + /** Pattern1 ::= PatVar Ascription + * | [‘-’] integerLiteral Ascription + * | [‘-’] floatingPointLiteral Ascription + * | Pattern2 */ def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() if in.isColon then val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] if !isVariableOrNumber then - warning(em"Only variable and number literal patterns can have type ascriptions") + report.gradualErrorOrMigrationWarning( + em"""Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + |are no longer supported. Remove the type ascription or move it to a separate variable pattern.""", + in.sourcePos(), + warnFrom = `3.3`, + errorFrom = future + ) in.nextToken() ascription(p, location) else p diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index b8b38cce92e4..fdbd9216f1b7 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -188,6 +188,7 @@ class CompilationTests { compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), + compileFile("tests/neg-custom-args/i10994.scala", defaultOptions.and("-source", "future")), ).checkExpectedErrors() } diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index cf6a6d053566..8e7de0efe19e 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -319,7 +319,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) -Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) +Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe)) + | [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe) + | [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe) + | Pattern2 Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 8d110f685f9d..bc709fb1f870 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -312,7 +312,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= Pattern2 [‘:’ RefinedType] +Pattern1 ::= PatVar ‘:’ RefinedType + | [‘-’] integerLiteral ‘:’ RefinedType + | [‘-’] floatingPointLiteral ‘:’ RefinedType + | Pattern2 Pattern2 ::= [id ‘@’] InfixPattern [‘*’] InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar diff --git a/tests/neg-custom-args/i10994.check b/tests/neg-custom-args/i10994.check new file mode 100644 index 000000000000..c540a04657c3 --- /dev/null +++ b/tests/neg-custom-args/i10994.check @@ -0,0 +1,7 @@ +-- Error: tests/neg-custom-args/i10994.scala:2:19 ---------------------------------------------------------------------- +2 | case (b: Boolean): Boolean => () // error + | ^ + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. diff --git a/tests/neg-custom-args/i10994.scala b/tests/neg-custom-args/i10994.scala new file mode 100644 index 000000000000..65695ccf4352 --- /dev/null +++ b/tests/neg-custom-args/i10994.scala @@ -0,0 +1,2 @@ +def foo = true match + case (b: Boolean): Boolean => () // error diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 36ac71b2e1e7..c461b76ea70b 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -59,7 +59,10 @@ -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- 13 | case List(1, _*3:) => // error // error | ^ - | Only variable and number literal patterns can have type ascriptions + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:22:20 --------------------------------------------------------------- 22 | val K(x @ _*) = k | ^ diff --git a/tests/pos/i10994.scala b/tests/pos/i10994.scala index 99ae647466b1..b7b6a3661649 100644 --- a/tests/pos/i10994.scala +++ b/tests/pos/i10994.scala @@ -1,2 +1,2 @@ def foo = true match - case (b: Boolean): Boolean => () + case (b: Boolean): Boolean => () // warning From 58256dd0fd1aa78ced4b1587275f7150d670d71e 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 90/97] Add changelog for 3.3.0-RC6 --- 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 9bae88a8eac9944e73edc9e7f0155e0bd5b381ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 12 May 2023 13:26:29 +0200 Subject: [PATCH 91/97] Release 3.3.0-RC6 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 03d482a83407..9109a925a450 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.2.2" - val baseVersion = "3.3.0-RC5" + val baseVersion = "3.3.0-RC6" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.0-RC4" + val previousDottyVersion = "3.3.0-RC5" object CompatMode { final val BinaryCompatible = 0 From 410e5df4ec58444ee5c63ac1ea4f5ddd8eb8d15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 May 2023 12:43:54 +0200 Subject: [PATCH 92/97] Set TASTy Version to 28.3.0 --- tasty/src/dotty/tools/tasty/TastyFormat.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 2d18923e1b0c..ac0357068c55 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -305,7 +305,7 @@ object TastyFormat { * is able to read final TASTy documents if the file's * `MinorVersion` is strictly less than the current value. */ - final val ExperimentalVersion: Int = 1 + final val ExperimentalVersion: Int = 0 /**This method implements a binary relation (`<:<`) between two TASTy versions. * From 92152f4225890c93cb6c2660dadfff9519d1f1e8 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 93/97] Add changelog for 3.3.0 --- 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 5879ff1caa82b4b5c32f67e88c85370c7fdbc5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 May 2023 13:35:45 +0200 Subject: [PATCH 94/97] Release 3.3.0 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 9109a925a450..5aca4ace8d6a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.2.2" - val baseVersion = "3.3.0-RC6" + val baseVersion = "3.3.0" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.0-RC5" + val previousDottyVersion = "3.2.2" object CompatMode { final val BinaryCompatible = 0 From 0fa1c91eb181e695e425bb8a022daf61c49ab214 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 95/97] Add info about 3.3 to source compat doc --- 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 724340e3bfd5c970655fe6e7f49d2d91697c96fe 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 96/97] Update docs/_docs/reference/language-versions/source-compatibility.md Co-authored-by: Jamie Thompson --- 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 232180f07a4415863b19f7e87ead852694effaf3 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 30 May 2023 16:12:44 +0200 Subject: [PATCH 97/97] Update source-compatibility.md reorder the source versions --- .../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 :