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 001/144] 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 002/144] 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 003/144] 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 004/144] 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 033a3b8ac9bac359f6883dd8c4027f8ca7ba4cc1 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Tue, 24 Jan 2023 23:22:54 +0100 Subject: [PATCH 005/144] Add default scaladoc settings to scaladoc artifact publishing --- project/Build.scala | 9 +++++++-- project/ScaladocGeneration.scala | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 5fab2b80229a..94deedc50582 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -839,6 +839,7 @@ object Build { "-sourcepath", (Compile / sourceDirectories).value.map(_.getAbsolutePath).distinct.mkString(File.pathSeparator), "-Yexplicit-nulls", ), + (Compile / doc / scalacOptions) ++= ScaladocConfigs.DefaultGenerationSettings.value.settings ) lazy val `scala3-library` = project.in(file("library")).asDottyLibrary(NonBootstrapped) @@ -1877,8 +1878,7 @@ object ScaladocConfigs { ) } - lazy val DefaultGenerationConfig = Def.task { - def distLocation = (dist / pack).value + lazy val DefaultGenerationSettings = Def.task { def projectVersion = version.value def socialLinks = SocialLinks(List( "github::https://github.com/lampepfl/dotty", @@ -1919,6 +1919,11 @@ object ScaladocConfigs { ) } + lazy val DefaultGenerationConfig = Def.task { + def distLocation = (dist / pack).value + DefaultGenerationSettings.value + } + lazy val Scaladoc = Def.task { DefaultGenerationConfig.value .add(UseJavacp(true)) diff --git a/project/ScaladocGeneration.scala b/project/ScaladocGeneration.scala index c6c4393c071f..fd972311da1d 100644 --- a/project/ScaladocGeneration.scala +++ b/project/ScaladocGeneration.scala @@ -141,6 +141,7 @@ object ScaladocGeneration { def remove[T <: Arg[_]: ClassTag]: GenerationConfig def withTargets(targets: Seq[String]): GenerationConfig def serialize: String + def settings: Seq[String] } object GenerationConfig { @@ -173,6 +174,9 @@ object ScaladocGeneration { ++ targets ).mkString(" ") + override def settings: Seq[String] = + args.map(_.serialize) ++ targets + private def argsWithout[T <: Arg[_]]( implicit tag: ClassTag[T] ): (Option[T], Seq[Arg[_]]) = args.foldLeft[(Option[T], Seq[Arg[_]])]((None, Seq.empty)) { 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 006/144] 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 007/144] 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 008/144] 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 009/144] 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 010/144] 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 011/144] 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 012/144] 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 013/144] 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 014/144] 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 015/144] 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 016/144] 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 017/144] 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 018/144] 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 019/144] 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 020/144] 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 021/144] 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 022/144] =?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 023/144] 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 024/144] 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 025/144] 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 026/144] 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 027/144] 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 028/144] 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 029/144] 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 030/144] 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 031/144] 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 032/144] 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 033/144] 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 034/144] 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 035/144] 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 036/144] 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 037/144] 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 038/144] 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 039/144] 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 040/144] 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 041/144] 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 042/144] 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 043/144] 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 044/144] 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 045/144] 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 046/144] 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 047/144] 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 048/144] 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 049/144] 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 050/144] 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 051/144] 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 052/144] 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 053/144] 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 054/144] 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 055/144] 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 056/144] 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 057/144] 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 058/144] 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 059/144] 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 060/144] 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 061/144] 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 062/144] 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 063/144] 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 064/144] 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 065/144] 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 066/144] 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 067/144] 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 068/144] 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 069/144] 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 070/144] 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 071/144] 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 072/144] 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 073/144] 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 074/144] 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 075/144] 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 076/144] 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 077/144] 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 078/144] 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 079/144] 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 080/144] 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 081/144] 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 082/144] 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 083/144] 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 084/144] 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 085/144] 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 086/144] 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 087/144] 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 088/144] 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 089/144] 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 090/144] * 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 091/144] 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 092/144] 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 093/144] 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 094/144] 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 095/144] 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 390f836c5ac0d8aaa30b331745e361b75139f68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 24 May 2023 13:29:23 +0200 Subject: [PATCH 096/144] Add changelog for 3.3.1-RC1 --- changelogs/3.3.1-RC1.md | 288 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 changelogs/3.3.1-RC1.md diff --git a/changelogs/3.3.1-RC1.md b/changelogs/3.3.1-RC1.md new file mode 100644 index 000000000000..4e52eb874891 --- /dev/null +++ b/changelogs/3.3.1-RC1.md @@ -0,0 +1,288 @@ +# Highlights of the release + +- Support records in JavaParsers [#16762](https://github.com/lampepfl/dotty/pull/16762) +- Port JVM backend refactor from Scala 2 [#15322](https://github.com/lampepfl/dotty/pull/15322) + +# Other changes and fixes + +## Backend + +- Disallow mixins where super calls bind to vals [#16908](https://github.com/lampepfl/dotty/pull/16908) +- Fix #15107: Avoid re-emitting a LineNumber after only LabelNodes. [#16813](https://github.com/lampepfl/dotty/pull/16813) + +## Coverage + +- Fix #17042: Preserve the shape of secondary ctors in instrumentCoverage. [#17111](https://github.com/lampepfl/dotty/pull/17111) + +## Default parameters + +- Dupe fix when finding default arg getters [#17058](https://github.com/lampepfl/dotty/pull/17058) + +## Documentation + +- Fix: ensure syntax blocks for ebnf are marked as such [#16837](https://github.com/lampepfl/dotty/pull/16837) + +## Erasure + +- Handle `@companionClass` and `@companionMethod` meta-annotations [#17091](https://github.com/lampepfl/dotty/pull/17091) + +## Extension Methods + +- Support extension methods imported from different objects [#17050](https://github.com/lampepfl/dotty/pull/17050) + +## GADTs + +- Fix tuple member selection so it works with GADT healing [#16766](https://github.com/lampepfl/dotty/pull/16766) +- Fix upper bound constraints, that are higher-kinded [#16744](https://github.com/lampepfl/dotty/pull/16744) +- Split out immutable GadtConstraint [#16602](https://github.com/lampepfl/dotty/pull/16602) + +## Implicits + +- Improve subtyping check for not yet eta-expanded higher kinded types [#17139](https://github.com/lampepfl/dotty/pull/17139) +- Harden tpd.Apply/TypeApply in case of errors [#16887](https://github.com/lampepfl/dotty/pull/16887) +- Try to be more subtle when inferring type parameters of class parents [#16896](https://github.com/lampepfl/dotty/pull/16896) +- Include `P` in the implicit scope of `P.this.type` [#17088](https://github.com/lampepfl/dotty/pull/17088) + +## Incremental Compilation + +- Fix under-compilation when the method type in a SAM changes [#16996](https://github.com/lampepfl/dotty/pull/16996) + +## Infrastructure + +- Set reference version to 3.3.0-RC6 [#17504](https://github.com/lampepfl/dotty/pull/17504) +- Fix #17119: Download Coursier from GitHub directly [#17141](https://github.com/lampepfl/dotty/pull/17141) + +## Inline + +- Remove NamedArg from inlined arguments [#17228](https://github.com/lampepfl/dotty/pull/17228) +- Don't generate a Select for a TermRef with NoPrefix [#16754](https://github.com/lampepfl/dotty/pull/16754) +- Prepare bodies of inline forwarders eagerly [#16757](https://github.com/lampepfl/dotty/pull/16757) +- Do not remove inline method implementations until PruneErasedDefs [#17408](https://github.com/lampepfl/dotty/pull/17408) + +## Java Interop + +- ClassfileParser: allow missing param names (for JDK 21) [#17536](https://github.com/lampepfl/dotty/pull/17536) + +## Linting + +- Improve -Wunused: locals, privates with unset vars warning #16639 [#17160](https://github.com/lampepfl/dotty/pull/17160) +- Fix wunused false positive when deriving alias type [#17157](https://github.com/lampepfl/dotty/pull/17157) +- Port `-Wnonunit-statement` setting for dotty [#16936](https://github.com/lampepfl/dotty/pull/16936) + +## Match Types + +- Normalize match type usage during implicit lookup [#17457](https://github.com/lampepfl/dotty/pull/17457) +- Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. [#17322](https://github.com/lampepfl/dotty/pull/17322) +- Fix match type reduction with wildcard type arguments [#17065](https://github.com/lampepfl/dotty/pull/17065) +- Fix check whether classtag can be generated for match types [#16708](https://github.com/lampepfl/dotty/pull/16708) + +## Parser + +- Allow lines starting with `.` to fall outside previous indentation widths [#17056](https://github.com/lampepfl/dotty/pull/17056) + +## Pattern Matching + +- Fix #11541: Specialize ClassTag[T] in exhaustivity check [#17385](https://github.com/lampepfl/dotty/pull/17385) +- Check outer class prefixes in type projections when pattern matching [#17136](https://github.com/lampepfl/dotty/pull/17136) +- Make unchecked cases non-`@unchecked` and non-unreachable [#16958](https://github.com/lampepfl/dotty/pull/16958) +- Fix #16899: Better handle X instanceOf P where X is T1 | T2 [#17382](https://github.com/lampepfl/dotty/pull/17382) + +## Pickling + +- ClassfileParser: Avoid cycle when accessing companion in inner class lookup [#16882](https://github.com/lampepfl/dotty/pull/16882) + +## Polyfunctions + +- Fix type aliases in beta-reduction of polyfunctions [#17054](https://github.com/lampepfl/dotty/pull/17054) + +## Quotes + +- Register `paramProxy` and `thisProxy` in `Quote` type [#17541](https://github.com/lampepfl/dotty/pull/17541) +- Only check newVal/newMethod privateWithin on -Xcheck-macros [#17437](https://github.com/lampepfl/dotty/pull/17437) +- Unencode quote and splice trees [#17342](https://github.com/lampepfl/dotty/pull/17342) +- Correctly type Expr.ofTupleFromSeq for arity > 22 [#17261](https://github.com/lampepfl/dotty/pull/17261) +- Use TermRef to distinguish distinct Type[T] instances [#17205](https://github.com/lampepfl/dotty/pull/17205) +- Check level consistency of SingletonTypeTree as a type [#17209](https://github.com/lampepfl/dotty/pull/17209) +- Fix splice type variable pattern detection [#17048](https://github.com/lampepfl/dotty/pull/17048) +- Avoid creation of `@SplicedType` quote local refrences [#17051](https://github.com/lampepfl/dotty/pull/17051) +- Dealias type references when healing types in quotes [#17049](https://github.com/lampepfl/dotty/pull/17049) +- Replace quoted type variables in signature of HOAS pattern result [#16951](https://github.com/lampepfl/dotty/pull/16951) +- Beta-reduce directly applied PolymorphicFunction [#16623](https://github.com/lampepfl/dotty/pull/16623) +- Use `Object.toString` for `quoted.{Expr, Type}` [#16663](https://github.com/lampepfl/dotty/pull/16663) +- Fix Splicer.isEscapedVariable [#16838](https://github.com/lampepfl/dotty/pull/16838) +- Fix references to class members defined in quotes [#17107](https://github.com/lampepfl/dotty/pull/17107) +- Handle pickled forward references in pickled expressions [#16855](https://github.com/lampepfl/dotty/pull/16855) +- Fix #16615 - crashes of path dependent types in spliced Type.of [#16773](https://github.com/lampepfl/dotty/pull/16773) +- Disallow local term references in staged types [#16362](https://github.com/lampepfl/dotty/pull/16362) +- Refactor level checking / type healing logic [#17082](https://github.com/lampepfl/dotty/pull/17082) +- Dealias quoted types when staging [#17059](https://github.com/lampepfl/dotty/pull/17059) +- Fix quotes with references to path dependent types [#17081](https://github.com/lampepfl/dotty/pull/17081) +- Make arguments order in quote hole deterministic [#17405](https://github.com/lampepfl/dotty/pull/17405) +- Only transform the body of the quote with QuoteTransformer [#17451](https://github.com/lampepfl/dotty/pull/17451) +- Place staged type captures in Quote AST [#17424](https://github.com/lampepfl/dotty/pull/17424) +- Add SplicePattern AST to parse and type quote pattern splices [#17396](https://github.com/lampepfl/dotty/pull/17396) + +## Reflection + +- -Xcheck-macros: add hint when a symbol in created twice [#16733](https://github.com/lampepfl/dotty/pull/16733) +- Assert that symbols created using reflect API have correct privateWithin symbols [#17352](https://github.com/lampepfl/dotty/pull/17352) +- Fix reflect.LambdaType type test [#16972](https://github.com/lampepfl/dotty/pull/16972) +- Improve `New`/`Select` -Ycheck message [#16746](https://github.com/lampepfl/dotty/pull/16746) +- Improve error message for CyclicReference in macros [#16749](https://github.com/lampepfl/dotty/pull/16749) +- Add reflect `defn.FunctionClass` overloads [#16849](https://github.com/lampepfl/dotty/pull/16849) + +## REPL + +- Always load REPL classes in macros including the output directory [#16866](https://github.com/lampepfl/dotty/pull/16866) + +## Reporting + +- Improve missing argument list error [#17126](https://github.com/lampepfl/dotty/pull/17126) +- Improve implicit parameter error message with aliases [#17125](https://github.com/lampepfl/dotty/pull/17125) +- Improve "constructor proxy shadows outer" handling [#17154](https://github.com/lampepfl/dotty/pull/17154) +- Clarify ambiguous reference error message [#16137](https://github.com/lampepfl/dotty/pull/16137) +- Hint about forbidden combination of implicit values and conversions [#16735](https://github.com/lampepfl/dotty/pull/16735) +- Attach explanation message to diagnostic message [#16787](https://github.com/lampepfl/dotty/pull/16787) +- Propagate implicit search errors from implicit macros [#16840](https://github.com/lampepfl/dotty/pull/16840) +- Detail UnapplyInvalidReturnType error message [#17167](https://github.com/lampepfl/dotty/pull/17167) +- Add way to debug -Xcheck-macros tree checking [#16973](https://github.com/lampepfl/dotty/pull/16973) +- Enrich and finesse compiler crash reporting [#17031](https://github.com/lampepfl/dotty/pull/17031) +- Allow @implicitNotFound messages as explanations [#16893](https://github.com/lampepfl/dotty/pull/16893) +- Include top-level symbols from same file in outer ambiguity error [#17033](https://github.com/lampepfl/dotty/pull/17033) +- Do not issue deprecation warnings when declaring deprecated case classes [#17165](https://github.com/lampepfl/dotty/pull/17165) + +## Scala-JS + +- Fix #17344: Make implicit references to this above dynamic imports explicit. [#17357](https://github.com/lampepfl/dotty/pull/17357) +- Fix #12621: Better error message for JS trait ctor param. [#16811](https://github.com/lampepfl/dotty/pull/16811) +- Fix #16801: Handle Closure's of s.r.FunctionXXL. [#16809](https://github.com/lampepfl/dotty/pull/16809) +- Fix #17549: Unify how Memoize and Constructors decide what fields need storing. [#17560](https://github.com/lampepfl/dotty/pull/17560) + +## Scaladoc + +- Feat: Add a blog configuration with yaml [#17214](https://github.com/lampepfl/dotty/pull/17214) +- Don't render the "$" for module [#17302](https://github.com/lampepfl/dotty/pull/17302) +- Fix: Add scrollbar to the sidebar [#17203](https://github.com/lampepfl/dotty/pull/17203) +- Scaladoc: fix crash when processing extends call [#17260](https://github.com/lampepfl/dotty/pull/17260) +- Fix: Modify the CSS so that the logo of the generated documentation is adaptive [#17172](https://github.com/lampepfl/dotty/pull/17172) +- Fix: Remove the duplicate parameter when generating the scaladoc. [#17097](https://github.com/lampepfl/dotty/pull/17097) +- Fix: padding top in mobile version [#17019](https://github.com/lampepfl/dotty/pull/17019) +- Fix: tap target of the menu in Mobile version [#17018](https://github.com/lampepfl/dotty/pull/17018) +- Scaladoc: Fix expand icon not changing on anchor link [#17053](https://github.com/lampepfl/dotty/pull/17053) +- Scaladoc: fix inkuire generation for PolyTypes [#17129](https://github.com/lampepfl/dotty/pull/17129) +- Re port scroll bar [#17463](https://github.com/lampepfl/dotty/pull/17463) +- Handle empty files and truncated YAML front matter [#17527](https://github.com/lampepfl/dotty/pull/17527) + +## SemanticDB + +- Make sure symbol exists before calling owner [#16860](https://github.com/lampepfl/dotty/pull/16860) +- Support LambdaType (convert from HKTypeLambda) [#16056](https://github.com/lampepfl/dotty/pull/16056) + +## Specification + +- Apply `class-shadowing.md` to the Spec [#16839](https://github.com/lampepfl/dotty/pull/16839) +- Adding base for future Spec into the compiler repo [#16825](https://github.com/lampepfl/dotty/pull/16825) + +## Standard Library + +- Optimization: avoid NotGiven allocations [#17090](https://github.com/lampepfl/dotty/pull/17090) + +## Tooling + +- Disable `ExtractSemanticDB` phase when writing to output directory defined as JAR. [#16790](https://github.com/lampepfl/dotty/pull/16790) +- Print owner of bind symbol with -Yprint-debug-owners [#16854](https://github.com/lampepfl/dotty/pull/16854) +- Small fixes to allow using Metals with scaladoc with sbt [#16816](https://github.com/lampepfl/dotty/pull/16816) + +## Transform + +- Move CrossVersionChecks before FirstTransform [#17301](https://github.com/lampepfl/dotty/pull/17301) +- Fix needsOuterIfReferenced [#17159](https://github.com/lampepfl/dotty/pull/17159) +- Drop incorrect super accessor in trait subclass [#17062](https://github.com/lampepfl/dotty/pull/17062) +- Generate toString only for synthetic companions of case classes [#16890](https://github.com/lampepfl/dotty/pull/16890) +- Check trait constructor for accessibility even if not called at Typer [#17094](https://github.com/lampepfl/dotty/pull/17094) +- Fix #17435: A simpler fix [#17436](https://github.com/lampepfl/dotty/pull/17436) + +## Typer + +- Preserve type bounds for inlined definitions in posttyper [#17190](https://github.com/lampepfl/dotty/pull/17190) +- Change logic to find members of recursive types [#17386](https://github.com/lampepfl/dotty/pull/17386) +- Recognize named arguments in isFunctionWithUnknownParamType [#17161](https://github.com/lampepfl/dotty/pull/17161) +- Better comparisons for type projections [#17092](https://github.com/lampepfl/dotty/pull/17092) +- Allow selectDynamic and applyDynamic to be extension methods [#17106](https://github.com/lampepfl/dotty/pull/17106) +- Fix use of accessibleFrom when finding default arg getters [#16977](https://github.com/lampepfl/dotty/pull/16977) +- Map class literal constant types [#16988](https://github.com/lampepfl/dotty/pull/16988) +- Always use adapted type in withDenotation [#16901](https://github.com/lampepfl/dotty/pull/16901) +- Restrict captureWildcards to only be used if needed [#16799](https://github.com/lampepfl/dotty/pull/16799) +- Don't capture wildcards if in closure or by-name [#16732](https://github.com/lampepfl/dotty/pull/16732) +- Infer: Don't minimise to Nothing if there's an upper bound [#16786](https://github.com/lampepfl/dotty/pull/16786) +- Perform Matchable check only if type test is needed [#16824](https://github.com/lampepfl/dotty/pull/16824) +- Don't eta expand unary varargs methods [#16892](https://github.com/lampepfl/dotty/pull/16892) +- Fix beta-reduction with `Nothing` and `null` args [#16938](https://github.com/lampepfl/dotty/pull/16938) +- Generate kind-correct wildcards when selecting from a wildcard [#17025](https://github.com/lampepfl/dotty/pull/17025) +- Fix #16405 ctd - wildcards prematurely resolving to Nothing [#16764](https://github.com/lampepfl/dotty/pull/16764) +- Test: add regression test for #7790 [#17473](https://github.com/lampepfl/dotty/pull/17473) +- Properly handle `AnyVal`s as refinement members of `Selectable`s [#16286](https://github.com/lampepfl/dotty/pull/16286) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0..3.3.1-RC1` these are: + +``` + 148 Nicolas Stucki + 65 Martin Odersky + 51 Szymon Rodziewicz + 49 Dale Wijnand + 49 Quentin Bernet + 38 Chris Kipp + 19 David Hua + 18 Lucas + 18 ysthakur + 15 Fengyun Liu + 15 PaweƂ Marks + 14 Guillaume Martres + 14 Jamie Thompson + 11 SĂ©bastien Doeraene + 9 TimothĂ©e Andres + 8 Kacper Korban + 7 Matt Bovel + 7 Som Snytt + 6 Julien Richard-Foy + 6 Lucas Leblanc + 5 MichaƂ PaƂka + 4 Anatolii Kmetiuk + 4 Guillaume Raffin + 4 Paul Coral + 4 Wojciech Mazur + 4 Yichen Xu + 3 Decel + 3 Jan Chyb + 2 Adrien Piquerez + 2 Arman Bilge + 2 Carl + 2 Florian3k + 2 Kenji Yoshida + 2 Michael Pilquist + 2 Natsu Kagami + 2 Seth Tisue + 2 Tomasz Godzik + 2 Vasil Vasilev + 2 Yadu Krishnan + 1 Bersier + 1 Flavio Brasil + 1 Jan-Pieter van den Heuvel + 1 Lukas Rytz + 1 Miles Yucht + 1 Mohammad Yousuf Minhaj Zia + 1 Ondra Pelech + 1 Philippus + 1 Rikito Taniguchi + 1 Simon R + 1 brandonspark + 1 github-actions[bot] + 1 liang3zy22 + 1 s.bazarsadaev + 1 Ɓukasz WroƄski + +``` From dfb23f95afa8bec461674140c99b19ea3a9ab010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 24 May 2023 13:30:49 +0200 Subject: [PATCH 097/144] Release 3.3.1-RC1 --- 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 226fc14acb39..39d559234868 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -290,7 +290,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 4 + final val MinorVersion: Int = 3 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing @@ -306,7 +306,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 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 098/144] 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 099/144] 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 100/144] 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 : From 8f019275f8dd54fc9554d00292e02fd331ce3427 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 30 May 2023 10:00:08 +0200 Subject: [PATCH 101/144] Dealias types in New before matching quotes Fixes #17606 --- .../scala/quoted/runtime/impl/QuoteMatcher.scala | 2 +- tests/pos-macros/i17606/Macros_1.scala | 14 ++++++++++++++ tests/pos-macros/i17606/Test_2.scala | 8 ++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i17606/Macros_1.scala create mode 100644 tests/pos-macros/i17606/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 5477628a30a3..bfa4c1c6d1f2 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -301,7 +301,7 @@ object QuoteMatcher { /* Match new */ case New(tpt1) => pattern match - case New(tpt2) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => matched + case New(tpt2) if tpt1.tpe.dealias.typeSymbol == tpt2.tpe.dealias.typeSymbol => matched case _ => notMatched /* Match this */ diff --git a/tests/pos-macros/i17606/Macros_1.scala b/tests/pos-macros/i17606/Macros_1.scala new file mode 100644 index 000000000000..245f2df66e7b --- /dev/null +++ b/tests/pos-macros/i17606/Macros_1.scala @@ -0,0 +1,14 @@ +package example + +import scala.quoted.* + +object A { + inline def f(inline a: Any): Boolean = ${ impl('a) } + + def impl(a: Expr[Any])(using Quotes): Expr[Boolean] = { + a match { + case '{ new String($x: Array[Byte]) } => Expr(true) + case _ => quotes.reflect.report.errorAndAbort("Expected match", a) + } + } +} diff --git a/tests/pos-macros/i17606/Test_2.scala b/tests/pos-macros/i17606/Test_2.scala new file mode 100644 index 000000000000..ebf535bc2ae9 --- /dev/null +++ b/tests/pos-macros/i17606/Test_2.scala @@ -0,0 +1,8 @@ +package example + +object Main { + def main(args: Array[String]): Unit = { + val x = A.f(new String(Array.empty[Byte])) + println(x) + } +} From 38265fca08ff605edc9df99f9628b2959c22a134 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 21 Jun 2023 11:10:59 +0200 Subject: [PATCH 102/144] sort language versions to match natural ordering also fixes the bullet-point under 3.3 to actually be a bullet point --- .../reference/language-versions/source-compatibility.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_docs/reference/language-versions/source-compatibility.md b/docs/_docs/reference/language-versions/source-compatibility.md index 5cb705a16b82..145c4a84d11b 100644 --- a/docs/_docs/reference/language-versions/source-compatibility.md +++ b/docs/_docs/reference/language-versions/source-compatibility.md @@ -17,16 +17,16 @@ The default Scala language syntax version currently supported by the Dotty compi - in conjunction with `-rewrite`, offer code rewrites from Scala 2.13 to 3.0. - [`3.0`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/0$.html), [`3.1`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/1$.html): the default set of features included in scala versions `3.0.0` to `3.1.3`. +- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. - [`3.2`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2$.html): the same as `3.0` and `3.1`, but in addition: - [stricter pattern bindings](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html) are now enabled (part of `future` in earlier `3.x` releases), producing warnings for refutable patterns. These warnings can be silenced to achieve the same runtime behavior, but in `future` they become errors and refutable patterns will not compile. - [Nonlocal returns](https://docs.scala-lang.org/scala3/reference/dropped-features/nonlocal-returns.html) now produce a warning upon usage (they are still an error under `future`). -- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. -- [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: - -[Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. - [`3.3-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3-migration$.html): the same as `3.3` +- [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: + - [Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. +- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. - [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.3`. Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. -- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. There are two ways to specify a language version : From 28d207dd9da5112204269d97d869b64e8cce9828 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Mon, 26 Jun 2023 16:19:22 +0200 Subject: [PATCH 103/144] Update indentation.md --- .../_docs/reference/other-new-features/indentation.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/_docs/reference/other-new-features/indentation.md b/docs/_docs/reference/other-new-features/indentation.md index 40e2fc6fb38c..75306ec6f90d 100644 --- a/docs/_docs/reference/other-new-features/indentation.md +++ b/docs/_docs/reference/other-new-features/indentation.md @@ -261,7 +261,8 @@ Indentation can be mixed freely with braces `{...}`, as well as brackets `[...]` For instance, consider: ```scala { - val x = f(x: Int, y => + val x = 4 + f(x: Int, y => x * ( y + 1 ) + @@ -270,13 +271,13 @@ For instance, consider: ) } ``` - - Here, the indentation width of the region enclosed by the braces is 3 (i.e. the indentation width of the + - Here, the indentation width of the region enclosed by the braces is 2 (i.e. the indentation width of the statement starting with `val`). - - The indentation width of the region in parentheses that follows `f` is also 3, since the opening + - The indentation width of the region in parentheses that follows `f` is also 2, since the opening parenthesis is not at the end of a line. - - The indentation width of the region in parentheses around `y + 1` is 9 + - The indentation width of the region in parentheses around `y + 1` is 6 (i.e. the indentation width of `y + 1`). - - Finally, the indentation width of the last region in parentheses starting with `(x` is 6 (i.e. the indentation width of the indented region following the `=>`. + - Finally, the indentation width of the last region in parentheses starting with `(x` is 4 (i.e. the indentation width of the indented region following the `=>`. ## Special Treatment of Case Clauses From 49680df3ecead55d5a53b065dbdf3f06b80b3812 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 25 Jun 2023 20:11:55 +0200 Subject: [PATCH 104/144] Fix accessibleType for package object prefixes Making a package object explicit re-computes the denotations of an overloaded method. So it should not be done after we have pruned down those denotations by an accessibility test. We now do it before checking accessibility. Fixes #15821 --- .../dotty/tools/dotc/core/Denotations.scala | 4 +-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 30 +++++++++++-------- tests/pos/i15821.scala | 9 ++++++ 3 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 tests/pos/i15821.scala diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 82368fd4dbf5..e56cc453d34d 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1269,8 +1269,8 @@ object Denotations { def hasAltWith(p: SingleDenotation => Boolean): Boolean = denot1.hasAltWith(p) || denot2.hasAltWith(p) def accessibleFrom(pre: Type, superAccess: Boolean)(using Context): Denotation = { - val d1 = denot1 accessibleFrom (pre, superAccess) - val d2 = denot2 accessibleFrom (pre, superAccess) + val d1 = denot1.accessibleFrom(pre, superAccess) + val d2 = denot2.accessibleFrom(pre, superAccess) if (!d1.exists) d2 else if (!d2.exists) d1 else derivedUnionDenotation(d1, d2) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6ac45cbcf04d..be6121e13209 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -77,21 +77,25 @@ trait TypeAssigner { * (2) in Java compilation units, `Object` is replaced by `defn.FromJavaObjectType` */ def accessibleType(tpe: Type, superAccess: Boolean)(using Context): Type = - tpe match + if ctx.isJava && tpe.isAnyRef then + defn.FromJavaObjectType + else tpe match case tpe: NamedType => - val pre = tpe.prefix - val name = tpe.name - def postProcess(d: Denotation) = - if ctx.isJava && tpe.isAnyRef then defn.FromJavaObjectType - else TypeOps.makePackageObjPrefixExplicit(tpe withDenot d) - val d = tpe.denot.accessibleFrom(pre, superAccess) - if d.exists then postProcess(d) + val tpe1 = TypeOps.makePackageObjPrefixExplicit(tpe) + if tpe1 ne tpe then + accessibleType(tpe1, superAccess) else - // it could be that we found an inaccessible private member, but there is - // an inherited non-private member with the same name and signature. - val d2 = pre.nonPrivateMember(name).accessibleFrom(pre, superAccess) - if reallyExists(d2) then postProcess(d2) - else NoType + val pre = tpe.prefix + val name = tpe.name + val d = tpe.denot.accessibleFrom(pre, superAccess) + if d eq tpe.denot then tpe + else if d.exists then tpe.withDenot(d) + else + // it could be that we found an inaccessible private member, but there is + // an inherited non-private member with the same name and signature. + val d2 = pre.nonPrivateMember(name).accessibleFrom(pre, superAccess) + if reallyExists(d2) then tpe.withDenot(d2) + else NoType case tpe => tpe /** Try to make `tpe` accessible, emit error if not possible */ diff --git a/tests/pos/i15821.scala b/tests/pos/i15821.scala new file mode 100644 index 000000000000..a72d13e07bc7 --- /dev/null +++ b/tests/pos/i15821.scala @@ -0,0 +1,9 @@ +def main = + foo.bar(42) + foo.bar + +package object foo { + def bar[F[_]]: Unit = ??? + def bar[F[_]](x: Int): Unit = ??? + private[foo] def bar[F[_]](x: Int)(implicit dummy: DummyImplicit): Unit = ??? +} From 186e4be054c0df229e4a97152635df788432a876 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 26 Jun 2023 14:53:36 +0200 Subject: [PATCH 105/144] Disable specs2 for now. --- .../scala/dotty/communitybuild/CommunityBuildTest.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 146ad6f4f951..8837f7319117 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -93,7 +93,12 @@ class CommunityBuildTestC: @Test def sconfig = projects.sconfig.run() @Test def shapeless = projects.shapeless.run() @Test def sourcecode = projects.sourcecode.run() - @Test def specs2 = projects.specs2.run() + + // Disabled. Currently fails in FutureMatchers.scala. The call to + // `checkResultFailure` goes to a protected method which is not accessible. + // I tried to fix it, but get test failures. + // @Test def specs2 = projects.specs2.run() + @Test def stdLib213 = projects.stdLib213.run() @Test def ujson = projects.ujson.run() @Test def upickle = projects.upickle.run() From 1451dc50ccc7bdf52501af02052aa1059487e393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 28 Jun 2023 15:36:01 +0200 Subject: [PATCH 106/144] Add changelog for 3.3.1-RC2 --- changelogs/3.3.1-RC2.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 changelogs/3.3.1-RC2.md diff --git a/changelogs/3.3.1-RC2.md b/changelogs/3.3.1-RC2.md new file mode 100644 index 000000000000..f21bfa074b66 --- /dev/null +++ b/changelogs/3.3.1-RC2.md @@ -0,0 +1,16 @@ +# Backported fixes + +- Dealias types in `New`` before matching quotes [#17615](https://github.com/lampepfl/dotty/pull/17615) +- Fix `accessibleType` for package object prefixes [#18057](https://github.com/lampepfl/dotty/pull/18057) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC1..3.3.1-RC2` these are: + +``` + 2 Martin Odersky + 2 PaweƂ Marks + 1 Nicolas Stucki +``` From c9bbcb0f0297b6097eff0dd28f9d5a5cae290e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 28 Jun 2023 15:52:38 +0200 Subject: [PATCH 107/144] Release 3.3.1-RC2 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 910ee7ef4f58..a2ea5ce1a596 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC1" + val baseVersion = "3.3.1-RC2" // 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" + val previousDottyVersion = "3.3.1-RC1" object CompatMode { final val BinaryCompatible = 0 From aed47fd78aec49aebf7d2d97b167e58c9c165cc2 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 5 Jul 2023 00:14:35 +0200 Subject: [PATCH 108/144] Add clause for protected visibility from package objects We usually have an access rule that the access to a protected member `foo` in class `C` must be from somewhere nested in a subclass of `C`. But that fails if the member is accessed from a package object `p.package`. In that case, the access does not need to be in the same object, it just has to be in package `p`. This clause was previously missing and is now added. Why was this only recently discovered? #18057 fixed an issue where toplevel protected members were always accessible because explicit package object prefixes were added after the accessibility check was done, and would re-establish the previous members without doing an accessibility check. The fix was done by adding package objects first, then doing he rest of the checks. But that also means that protected toplevel objects now get checked as members of their synthetic package object instead of as members of their package. The change here also makes specs2 compile again. --- .../dotty/communitybuild/CommunityBuildTest.scala | 6 +----- .../dotty/tools/dotc/core/SymDenotations.scala | 11 +++++++---- tests/pos/i18124/definition.scala | 15 +++++++++++++++ tests/pos/i18124/usage.scala | 8 ++++++++ 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i18124/definition.scala create mode 100644 tests/pos/i18124/usage.scala diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 8837f7319117..bf6b6d431509 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -93,11 +93,7 @@ class CommunityBuildTestC: @Test def sconfig = projects.sconfig.run() @Test def shapeless = projects.shapeless.run() @Test def sourcecode = projects.sourcecode.run() - - // Disabled. Currently fails in FutureMatchers.scala. The call to - // `checkResultFailure` goes to a protected method which is not accessible. - // I tried to fix it, but get test failures. - // @Test def specs2 = projects.specs2.run() + @Test def specs2 = projects.specs2.run() @Test def stdLib213 = projects.stdLib213.run() @Test def ujson = projects.ujson.run() diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index aa97435d64bb..988a37be4388 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -907,10 +907,13 @@ object SymDenotations { false val cls = owner.enclosingSubClass if !cls.exists then - val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass - fail(i""" - | Access to protected $this not permitted because enclosing ${encl.showLocated} - | is not a subclass of ${owner.showLocated} where target is defined""") + if pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) then + true + else + val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass + fail(i""" + | Access to protected $this not permitted because enclosing ${encl.showLocated} + | is not a subclass of ${owner.showLocated} where target is defined""") else if isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) then // allow accesses to types from arbitrary subclasses fixes #4737 // don't perform this check for static members diff --git a/tests/pos/i18124/definition.scala b/tests/pos/i18124/definition.scala new file mode 100644 index 000000000000..1377c94fe7cd --- /dev/null +++ b/tests/pos/i18124/definition.scala @@ -0,0 +1,15 @@ +// definition.scala +package oolong.bson: + + trait BsonValue + protected def merge( + base: BsonValue, + patch: BsonValue, + arraySubvalues: Boolean = false + ): BsonValue = ??? + + private def foo: Int = 1 + + package inner: + protected[bson] def bar = 2 + diff --git a/tests/pos/i18124/usage.scala b/tests/pos/i18124/usage.scala new file mode 100644 index 000000000000..0bc0417c01ad --- /dev/null +++ b/tests/pos/i18124/usage.scala @@ -0,0 +1,8 @@ +// usage.scala +package oolong.bson + +extension (bv: BsonValue) + def :+(other: BsonValue): BsonValue = merge(other, bv, false) + +val x = foo +val y = inner.bar From 9cae4e8af5a54916b9fa128e620bd53cf54d3e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 5 Jul 2023 17:58:03 +0200 Subject: [PATCH 109/144] Add changelog for 3.3.1-RC3 --- changelogs/3.3.1-RC3.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 changelogs/3.3.1-RC3.md diff --git a/changelogs/3.3.1-RC3.md b/changelogs/3.3.1-RC3.md new file mode 100644 index 000000000000..006d887c4f49 --- /dev/null +++ b/changelogs/3.3.1-RC3.md @@ -0,0 +1,15 @@ +# Backported fixes + +- Add clause for protected visibility from package objects [#18134](https://github.com/lampepfl/dotty/pull/18134) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC2..3.3.1-RC3` these are: + +``` + 2 PaweƂ Marks + 1 Martin Odersky + +``` From 161de6e8e7b0e8f4fd59406bc9c1b9c79c6a634b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 5 Jul 2023 17:59:29 +0200 Subject: [PATCH 110/144] Release 3.3.1-RC3 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index a2ea5ce1a596..1d4f2c7350a5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC2" + val baseVersion = "3.3.1-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.3.1-RC1" + val previousDottyVersion = "3.3.1-RC2" object CompatMode { final val BinaryCompatible = 0 From 011e6674f0b79a84c4b68a749c73b0c91d7c036e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 May 2023 08:54:12 +0200 Subject: [PATCH 111/144] Revert "Include top-level symbols from same file in outer ambiguity error" This reverts commit 7d4e103a941a30306ddde28a11f8bc3a8841acf8. Closes #17433 --- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ++++------------ tests/neg/ambiref.check | 16 -------------- tests/neg/ambiref.scala | 22 +------------------ tests/pos-special/fatal-warnings/i9260.scala | 2 +- tests/run/protectedacc.scala | 2 +- 5 files changed, 7 insertions(+), 54 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2e7444af8e96..cb23262d1410 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -408,16 +408,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Does reference `tp` refer only to inherited symbols? def isInherited(denot: Denotation) = def isCurrent(mbr: SingleDenotation): Boolean = - !mbr.symbol.exists || mbr.symbol.owner == ctx.owner || ctx.owner.is(Package) + !mbr.symbol.exists || mbr.symbol.owner == ctx.owner denot match case denot: SingleDenotation => !isCurrent(denot) case denot => !denot.hasAltWith(isCurrent) - /* It is an error if an identifier x is available as an inherited member in an inner scope - * and the same name x is defined in an outer scope in the same source file, unless - * the inherited member (has an overloaded alternative that) coincides with - * (an overloaded alternative of) the definition x. - */ def checkNoOuterDefs(denot: Denotation, last: Context, prevCtx: Context): Unit = def sameTermOrType(d1: SingleDenotation, d2: Denotation) = d2.containsSym(d1.symbol) || d2.hasUniqueSym && { @@ -434,15 +429,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val owner = outer.owner if (owner eq last.owner) && (outer.scope eq last.scope) then checkNoOuterDefs(denot, outer, prevCtx) - else if !owner.isRoot then - val found = - if owner.is(Package) then - owner.denot.asClass.membersNamed(name) - .filterWithPredicate(d => !d.symbol.is(Package) && d.symbol.source == denot.symbol.source) - else - val scope = if owner.isClass then owner.info.decls else outer.scope - scope.denotsNamed(name) - val competing = found.filterWithFlags(required, excluded | Synthetic) + else if !owner.is(Package) then + val scope = if owner.isClass then owner.info.decls else outer.scope + val competing = scope.denotsNamed(name).filterWithFlags(required, excluded) if competing.exists then val symsMatch = competing .filterWithPredicate(sd => sameTermOrType(sd, denot)) diff --git a/tests/neg/ambiref.check b/tests/neg/ambiref.check index 32b4078f1346..5d701b3b3b71 100644 --- a/tests/neg/ambiref.check +++ b/tests/neg/ambiref.check @@ -30,19 +30,3 @@ | and inherited subsequently in class E | | longer explanation available when compiling with `-explain` --- [E049] Reference Error: tests/neg/ambiref.scala:43:10 --------------------------------------------------------------- -43 | println(global) // error - | ^^^^^^ - | Reference to global is ambiguous. - | It is both defined in package - | and inherited subsequently in object D - | - | longer explanation available when compiling with `-explain` --- [E049] Reference Error: tests/neg/ambiref.scala:49:16 --------------------------------------------------------------- -49 | def t = new T { } // error - | ^ - | Reference to T is ambiguous. - | It is both defined in package p - | and inherited subsequently in class C - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg/ambiref.scala b/tests/neg/ambiref.scala index bb48997cd465..e7a5d5efbd7e 100644 --- a/tests/neg/ambiref.scala +++ b/tests/neg/ambiref.scala @@ -40,24 +40,4 @@ val global = 0 class C: val global = 1 object D extends C: - println(global) // error - -package p: - class T - trait P { trait T } - class C extends P: - def t = new T { } // error - -package scala: - trait P { trait Option[+A] } - class C extends P: - def t = new Option[String] { } // OK, competing scala.Option is not defined in the same compilation unit - -object test5: - class Mu // generates a synthetic companion object with an apply method - trait A { - val Mu = 1 - } - trait B extends A { - def t = Mu // don't warn about synthetic companion - } + println(global) // OK, since global is defined in package \ No newline at end of file diff --git a/tests/pos-special/fatal-warnings/i9260.scala b/tests/pos-special/fatal-warnings/i9260.scala index 0392c1c96fa8..df548f393eea 100644 --- a/tests/pos-special/fatal-warnings/i9260.scala +++ b/tests/pos-special/fatal-warnings/i9260.scala @@ -10,7 +10,7 @@ end AstImpl object untpd extends AstImpl[Null]: - def DefDef(ast: this.Ast): DefDef = ast match + def DefDef(ast: Ast): DefDef = ast match case ast: DefDef => ast end untpd diff --git a/tests/run/protectedacc.scala b/tests/run/protectedacc.scala index 85aa3438faa3..a08e7201fd15 100644 --- a/tests/run/protectedacc.scala +++ b/tests/run/protectedacc.scala @@ -134,7 +134,7 @@ package p { abstract class X[T] extends PolyA[T] { - trait Inner extends this.B { + trait Inner extends B { def self: T; def self2: Node; def getB: Inner; From bf10893d6bc034c5aafccecd47d38b69c0d8f274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 11 Jul 2023 12:58:07 +0200 Subject: [PATCH 112/144] Add changelog for 3.3.1-RC4 --- changelogs/3.3.1-RC3.md | 2 +- changelogs/3.3.1-RC4.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 changelogs/3.3.1-RC4.md diff --git a/changelogs/3.3.1-RC3.md b/changelogs/3.3.1-RC3.md index 006d887c4f49..eb19f40b10dc 100644 --- a/changelogs/3.3.1-RC3.md +++ b/changelogs/3.3.1-RC3.md @@ -10,6 +10,6 @@ According to `git shortlog -sn --no-merges 3.3.1-RC2..3.3.1-RC3` these are: ``` 2 PaweƂ Marks - 1 Martin Odersky + 1 Nicolas Stucki ``` diff --git a/changelogs/3.3.1-RC4.md b/changelogs/3.3.1-RC4.md new file mode 100644 index 000000000000..7d95e0258fad --- /dev/null +++ b/changelogs/3.3.1-RC4.md @@ -0,0 +1,15 @@ +# Backported fixes + +- Revert "Include top-level symbols from same file in outer ambiguity error" [#17438](https://github.com/lampepfl/dotty/pull/17438) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC3..3.3.1-RC4` these are: + +``` + 2 PaweƂ Marks + 1 Nicolas Stucki + +``` From 555df5304aa882e67e08c917ff4924ab5947d295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 11 Jul 2023 13:00:08 +0200 Subject: [PATCH 113/144] Release 3.3.1-RC4 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 1d4f2c7350a5..a60932eb9e30 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC3" + val baseVersion = "3.3.1-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.1-RC2" + val previousDottyVersion = "3.3.1-RC3" object CompatMode { final val BinaryCompatible = 0 From c54bf671b0293890a26a21d0b6325ad1a117615d Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 14 Jul 2023 11:42:23 +0200 Subject: [PATCH 114/144] Update link to point to correct section In the reference, in Erased Definitions, link pointed to the Inline page, even though the content is in Compile Time Operations --- docs/_docs/reference/experimental/erased-defs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_docs/reference/experimental/erased-defs.md b/docs/_docs/reference/experimental/erased-defs.md index 28455f26cdc0..548b9c11bc0b 100644 --- a/docs/_docs/reference/experimental/erased-defs.md +++ b/docs/_docs/reference/experimental/erased-defs.md @@ -161,8 +161,8 @@ object Machine: // State must be Off ``` -Note that in [Inline](../metaprogramming/inline.md) we discussed `erasedValue` and inline -matches. `erasedValue` is implemented with `erased`, so the state machine above +Note that in [Compile-time operations](../metaprogramming/compiletime-ops.md#erasedvalue) we discussed `erasedValue` and inline +matches. `erasedValue` is internally implemented with `erased` (and is not experimental), so the state machine above can be encoded as follows: ```scala From e1233d80c5a870ebb51c72e4b26a8fc8004b3774 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Jul 2023 14:32:36 +0200 Subject: [PATCH 115/144] Heal stage inconsistent prefixes of type projections Fixes #17293 --- compiler/src/dotty/tools/dotc/staging/HealType.scala | 2 +- tests/pos-macros/i17293.scala | 12 ++++++++++++ tests/pos-macros/i17293b.scala | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i17293.scala create mode 100644 tests/pos-macros/i17293b.scala diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 023271960b40..7d3ca0ad2f63 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -46,7 +46,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { case prefix: TermRef if tp.symbol.isTypeSplice => checkNotWildcardSplice(tp) if level == 0 then tp else getTagRef(prefix) - case _: NamedType | _: ThisType | NoPrefix => + case _: TermRef | _: ThisType | NoPrefix => if levelInconsistentRootOfPath(tp).exists then tryHeal(tp) else diff --git a/tests/pos-macros/i17293.scala b/tests/pos-macros/i17293.scala new file mode 100644 index 000000000000..57eba1181903 --- /dev/null +++ b/tests/pos-macros/i17293.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +trait OuterTrait { + trait X +} + +def exampleMacro[T <: OuterTrait: Type](expr: Expr[T])(using Quotes): Expr[OuterTrait#X] = { + '{ + val prefix: T = ${ expr } + new prefix.X {} + } +} diff --git a/tests/pos-macros/i17293b.scala b/tests/pos-macros/i17293b.scala new file mode 100644 index 000000000000..a8b73ba6176b --- /dev/null +++ b/tests/pos-macros/i17293b.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +trait OuterTrait { self => + trait X + + def exampleMacro[T <: self.type: Type](expr: Expr[T])(using Quotes): Expr[self.X] = { + '{ + val prefix: T = ${ expr } + new prefix.X {} + } + } +} \ No newline at end of file From 5f2450aa8ae3c00f6f52eb6a2dbe2427fe0ae6a8 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Tue, 25 Jul 2023 12:56:09 +0200 Subject: [PATCH 116/144] Fix regression with Overloaded methods returning Functions Before the regression, FunctionOf unapply would not try dealiasing, meaning that an aliased function type would be handled by a general case. To fix that, instead of handling Function types separately when filtering overloaded methods in `resolveOverloaded1`, we allow to fallback to the general case if the previous one returns nothing. Along with fixing the regression, this also improves other cases, one of which was added to the test. Readd a separate FunctionOf case, but with a fallback --- .../dotty/tools/dotc/typer/Applications.scala | 52 ++++++++++--------- tests/pos/i17245.scala | 20 +++++++ 2 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 tests/pos/i17245.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index fbed4b77d3fe..cb6aec26406a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -2062,31 +2062,35 @@ trait Applications extends Compatibility { if isDetermined(alts2) then alts2 else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1) - case defn.FunctionOf(args, resultType, _) => - narrowByTypes(alts, args, resultType) - case pt => - val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false)) - if (compat.isEmpty) - /* - * the case should not be moved to the enclosing match - * since SAM type must be considered only if there are no candidates - * For example, the second f should be chosen for the following code: - * def f(x: String): Unit = ??? - * def f: java.io.OutputStream = ??? - * new java.io.ObjectOutputStream(f) - */ - pt match { - case SAMType(mtp) => - narrowByTypes(alts, mtp.paramInfos, mtp.resultType) - case _ => - // pick any alternatives that are not methods since these might be convertible - // to the expected type, or be used as extension method arguments. - val convertible = alts.filterNot(alt => - normalize(alt, IgnoredProto(pt)).widenSingleton.isInstanceOf[MethodType]) - if convertible.length == 1 then convertible else compat - } - else compat + val compat0 = pt match + case defn.FunctionOf(args, resType, _) => + narrowByTypes(alts, args, resType) + case _ => + Nil + if (compat0.isEmpty) then + val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false)) + if (compat.isEmpty) + /* + * the case should not be moved to the enclosing match + * since SAM type must be considered only if there are no candidates + * For example, the second f should be chosen for the following code: + * def f(x: String): Unit = ??? + * def f: java.io.OutputStream = ??? + * new java.io.ObjectOutputStream(f) + */ + pt match { + case SAMType(mtp) => + narrowByTypes(alts, mtp.paramInfos, mtp.resultType) + case _ => + // pick any alternatives that are not methods since these might be convertible + // to the expected type, or be used as extension method arguments. + val convertible = alts.filterNot(alt => + normalize(alt, IgnoredProto(pt)).widenSingleton.isInstanceOf[MethodType]) + if convertible.length == 1 then convertible else compat + } + else compat + else compat0 } /** The type of alternative `alt` after instantiating its first parameter diff --git a/tests/pos/i17245.scala b/tests/pos/i17245.scala new file mode 100644 index 000000000000..3b5b3a74108d --- /dev/null +++ b/tests/pos/i17245.scala @@ -0,0 +1,20 @@ +import scala.reflect.ClassTag + +trait MockSettings + +object Mockito { + def mock[T : ClassTag]: T = ??? + def mock[T : ClassTag](settings: MockSettings): T = ??? +} + +trait Channel +type OnChannel = Channel => Any + +@main def Test = + val case1: OnChannel = Mockito.mock[OnChannel] + val case2: OnChannel = Mockito.mock + val case3 = Mockito.mock[OnChannel] + val case4: OnChannel = Mockito.mock[OnChannel](summon[ClassTag[OnChannel]]) + + // not a regressive case, but an added improvement with the fix for the above + val case5: Channel => Any = Mockito.mock[Channel => Any] From b85cbb5a3a64cd7b21bf6b2cbd3f75c0f11db8fd Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 17 Jul 2023 18:34:16 +0200 Subject: [PATCH 117/144] Disallow taking singleton types of packages again Fixes #18109 --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 13 ++++++++----- tests/neg/i18109.scala | 11 +++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 tests/neg/i18109.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index b2ab5332c3b2..df5639b50302 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -748,13 +748,16 @@ object Checking { if sym.isNoValue && !ctx.isJava then report.error(JavaSymbolIsNotAValue(sym), tree.srcPos) + /** Check that `tree` refers to a value, unless `tree` is selected or applied + * (singleton types x.type don't count as selections). + */ def checkValue(tree: Tree, proto: Type)(using Context): tree.type = tree match - case tree: RefTree - if tree.name.isTermName - && !proto.isInstanceOf[SelectionProto] - && !proto.isInstanceOf[FunOrPolyProto] => - checkValue(tree) + case tree: RefTree if tree.name.isTermName => + proto match + case _: SelectionProto if proto ne SingletonTypeProto => // no value check + case _: FunOrPolyProto => // no value check + case _ => checkValue(tree) case _ => tree diff --git a/tests/neg/i18109.scala b/tests/neg/i18109.scala new file mode 100644 index 000000000000..7df13b0c36ff --- /dev/null +++ b/tests/neg/i18109.scala @@ -0,0 +1,11 @@ +package foo {} + +package bar { + object Test { + def qux[A] = 123 + def main(args: Array[String]): Unit = { + val y = qux[foo.type] // error + val x = valueOf[foo.type] // error + } + } +} \ No newline at end of file From 110c91f5831b24c6751b30ed45f76c436be9db04 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 7 Aug 2023 18:58:54 +0200 Subject: [PATCH 118/144] A slightly more conservative version of #14128 Two changes - Fix `hasUpperBound` to work correctly for higher-kinded types - A more conservative fix in `IsFullyDefinedAccumulator`. We now maintain the symmetry that - if variance < 0, we maximize - if variance > 0 (and Nothing is admissible) we minimize - only if variance = 0, we use the upper bound as a tie breaker Previously, we maximized even if variance > 0 if there was an upper but no lower bound. But that was asymmetric since there is no corresponding case where we minimize at variance < 0 if there is a lower but no upper bound. --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 ++++++- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 73a64f2c1b8f..bb4fd02816a6 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -246,6 +246,11 @@ object Types { case _ => false } + /** Is this type exactly `Any`, or a type lambda ending in `Any`? */ + def isTopOfSomeKind(using Context): Boolean = dealias match + case tp: TypeLambda => tp.resType.isTopOfSomeKind + case _ => isExactlyAny + def isBottomType(using Context): Boolean = if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes then hasClassSymbol(defn.NothingClass) else isBottomTypeAfterErasure @@ -4813,7 +4818,7 @@ object Types { def hasLowerBound(using Context): Boolean = !currentEntry.loBound.isExactlyNothing /** For uninstantiated type variables: Is the upper bound different from Any? */ - def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.finalResultType.isExactlyAny + def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.isTopOfSomeKind /** Unwrap to instance (if instantiated) or origin (if not), until result * is no longer a TypeVar diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 0e1c41ceef74..4d027b8750e0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -187,7 +187,11 @@ object Inferencing { // else hold off instantiating unbounded unconstrained variable else if direction != 0 then instantiate(tvar, fromBelow = direction < 0) - else if variance >= 0 && (force.ifBottom == IfBottom.ok && !tvar.hasUpperBound || tvar.hasLowerBound) then + else if variance >= 0 && tvar.hasLowerBound then + instantiate(tvar, fromBelow = true) + else if (variance > 0 || variance == 0 && !tvar.hasUpperBound) + && force.ifBottom == IfBottom.ok + then // if variance == 0, prefer upper bound if one is given instantiate(tvar, fromBelow = true) else if variance >= 0 && force.ifBottom == IfBottom.fail then fail = true From 232c5f448f49406d6bf68ab4f5b230e4cf6aaf39 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 7 Aug 2023 11:05:44 +0100 Subject: [PATCH 119/144] Show Implicit Candidate & RefAndLevel --- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 7 +++++++ compiler/src/dotty/tools/dotc/printing/Printer.scala | 5 ++++- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 6 ++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index f3540502597c..700b3fbf525f 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -640,6 +640,13 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (pos.source.exists) s"${pos.source.file.name}:${pos.line + 1}" else s"(no source file, offset = ${pos.span.point})" + def toText(cand: Candidate): Text = + "Cand(" + ~ toTextRef(cand.ref) + ~ (if cand.isConversion then " conv" else "") + ~ (if cand.isExtension then " ext" else "") + ~ Str(" L" + cand.level) ~ ")" + def toText(result: SearchResult): Text = result match { case result: SearchSuccess => "SearchSuccess: " ~ toText(result.ref) ~ " via " ~ toText(result.tree) diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index 697ab063a646..ab0c867ec31f 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -7,7 +7,7 @@ import Texts._, ast.Trees._ import Types.{Type, SingletonType, LambdaParam}, Symbols.Symbol, Scopes.Scope, Constants.Constant, Names.Name, Denotations._, Annotations.Annotation, Contexts.Context -import typer.Implicits.SearchResult +import typer.Implicits.* import util.SourcePosition import typer.ImportInfo @@ -153,6 +153,9 @@ abstract class Printer { /** Textual representation of source position */ def toText(pos: SourcePosition): Text + /** Textual representation of implicit candidates. */ + def toText(cand: Candidate): Text + /** Textual representation of implicit search result */ def toText(result: SearchResult): Text diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c6795ed25a0e..66f400d7eae0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -49,17 +49,19 @@ object Implicits: } /** Both search candidates and successes are references with a specific nesting level. */ - sealed trait RefAndLevel { + sealed trait RefAndLevel extends Showable { def ref: TermRef def level: Int } /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */ - case class Candidate(implicitRef: ImplicitRef, kind: Candidate.Kind, level: Int) extends RefAndLevel { + case class Candidate(implicitRef: ImplicitRef, kind: Candidate.Kind, level: Int) extends RefAndLevel with Showable { def ref: TermRef = implicitRef.underlyingRef def isExtension = (kind & Candidate.Extension) != 0 def isConversion = (kind & Candidate.Conversion) != 0 + + def toText(printer: Printer): Text = printer.toText(this) } object Candidate { type Kind = Int From 48c994c7e82c2fe4be4e7bfa294ce5afc3148270 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 24 Jul 2023 15:34:17 +0100 Subject: [PATCH 120/144] Record failures to adapt application arguments --- .../tools/dotc/core/OrderingConstraint.scala | 4 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- tests/neg-macros/i6762.scala | 2 +- tests/neg/enum-values.check | 8 ++-- tests/neg/enumsAccess.scala | 2 +- tests/neg/i6779.check | 2 +- tests/neg/recursive-lower-constraint.scala | 2 +- tests/neg/syntax-error-recovery.check | 6 --- tests/neg/syntax-error-recovery.scala | 2 +- tests/pos/i18163.orig.scala | 40 +++++++++++++++++++ tests/pos/i18163.scala | 21 ++++++++++ 11 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 tests/pos/i18163.orig.scala create mode 100644 tests/pos/i18163.scala diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index faea30390d2b..0328cea9b3ca 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -344,7 +344,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, if newSet.isEmpty then deps.remove(referenced) else deps.updated(referenced, newSet) - def traverse(t: Type) = t match + def traverse(t: Type) = try + t match case param: TypeParamRef => if hasBounds(param) then if variance >= 0 then coDeps = update(coDeps, param) @@ -356,6 +357,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, seen += tp traverse(tp.ref) case _ => traverseChildren(t) + catch case ex: Throwable => handleRecursive("adjust", t.show, ex) end Adjuster /** Adjust dependencies to account for the delta of previous entry `prevEntry` diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index fbed4b77d3fe..a9376444a911 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -844,7 +844,7 @@ trait Applications extends Compatibility { var typedArgs = typedArgBuf.toList def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = - if (!success) app0.withType(UnspecifiedErrorType) + if (!success || typedArgs.exists(_.tpe.isError)) app0.withType(UnspecifiedErrorType) else { if !sameSeq(args, orderedArgs) && !isJavaAnnotConstr(methRef.symbol) diff --git a/tests/neg-macros/i6762.scala b/tests/neg-macros/i6762.scala index a8df289b26c2..054945e213d6 100644 --- a/tests/neg-macros/i6762.scala +++ b/tests/neg-macros/i6762.scala @@ -2,4 +2,4 @@ import scala.quoted.* type G[X] case class Foo[T](x: T) -def f(word: String)(using Quotes): Expr[Foo[G[String]]] = '{Foo(${Expr(word)})} // error // error +def f(word: String)(using Quotes): Expr[Foo[G[String]]] = '{Foo(${Expr(word)})} // error diff --git a/tests/neg/enum-values.check b/tests/neg/enum-values.check index 37990e8f312e..23337de1b2c4 100644 --- a/tests/neg/enum-values.check +++ b/tests/neg/enum-values.check @@ -24,8 +24,8 @@ | | failed with: | - | Found: Array[example.Tag[?]] - | Required: Array[example.ListLike[?]] + | Found: example.ListLike.type + | Required: Nothing -- [E008] Not Found Error: tests/neg/enum-values.scala:34:52 ----------------------------------------------------------- 34 | val typeCtorsK: Array[TypeCtorsK[?]] = TypeCtorsK.values // error | ^^^^^^^^^^^^^^^^^ @@ -38,8 +38,8 @@ | | failed with: | - | Found: Array[example.Tag[?]] - | Required: Array[example.TypeCtorsK[?[_$1]]] + | Found: example.TypeCtorsK.type + | Required: Nothing -- [E008] Not Found Error: tests/neg/enum-values.scala:36:6 ------------------------------------------------------------ 36 | Tag.valueOf("Int") // error | ^^^^^^^^^^^ diff --git a/tests/neg/enumsAccess.scala b/tests/neg/enumsAccess.scala index 18b91b346b6a..8a8e9af8910f 100644 --- a/tests/neg/enumsAccess.scala +++ b/tests/neg/enumsAccess.scala @@ -63,7 +63,7 @@ object test5 { enum E5[T](x: T) { case C3() extends E5[INT](defaultX)// error: illegal reference // error: illegal reference case C4 extends E5[INT](defaultX) // error: illegal reference // error: illegal reference - case C5 extends E5[E5[_]](E5.this) // error: type mismatch + case C5 extends E5[E5[_]](E5.this) // error: cannot be instantiated // error: conflicting base types // error: type mismatch } object E5 { diff --git a/tests/neg/i6779.check b/tests/neg/i6779.check index 8e05c22eb640..f1e1b9d5557b 100644 --- a/tests/neg/i6779.check +++ b/tests/neg/i6779.check @@ -11,7 +11,7 @@ | value f is not a member of T. | An extension method was tried, but could not be fully constructed: | - | Test.f[G[T]](x)(given_Stuff) + | Test.f[G[T]](x) | | failed with: | diff --git a/tests/neg/recursive-lower-constraint.scala b/tests/neg/recursive-lower-constraint.scala index 8009ab5fce6e..cf45d8b95171 100644 --- a/tests/neg/recursive-lower-constraint.scala +++ b/tests/neg/recursive-lower-constraint.scala @@ -3,5 +3,5 @@ class Bar extends Foo[Bar] class A { def foo[T <: Foo[T], U >: Foo[T] <: T](x: T): T = x - foo(new Bar) // error + foo(new Bar) // error // error } diff --git a/tests/neg/syntax-error-recovery.check b/tests/neg/syntax-error-recovery.check index 0bf626210fed..18d877833d79 100644 --- a/tests/neg/syntax-error-recovery.check +++ b/tests/neg/syntax-error-recovery.check @@ -94,12 +94,6 @@ | Not found: bam | | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/syntax-error-recovery.scala:61:10 ------------------------------------------------- -61 | println(bam) // error - | ^^^ - | Not found: bam - | - | longer explanation available when compiling with `-explain` -- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:7:2 ------------------------------------------- 6 | 2 7 | } diff --git a/tests/neg/syntax-error-recovery.scala b/tests/neg/syntax-error-recovery.scala index 775abeb97bdb..b6663cc9c70a 100644 --- a/tests/neg/syntax-error-recovery.scala +++ b/tests/neg/syntax-error-recovery.scala @@ -58,5 +58,5 @@ object Test2: def foo5(x: Int) = foo2(foo2(,) // error // error - println(bam) // error + println(bam) // error \ No newline at end of file diff --git a/tests/pos/i18163.orig.scala b/tests/pos/i18163.orig.scala new file mode 100644 index 000000000000..eb0627254156 --- /dev/null +++ b/tests/pos/i18163.orig.scala @@ -0,0 +1,40 @@ +import scala.language.implicitConversions + +// We do have 2 `contramap` functions, one provided via `LoggerSyntax` other via `Contravariant.Ops` +// `ContravariantMonoidal` given instances are not used, and they do not match our type. Code fails when we have at least 2 instances of them +// Removal of `import catsSyntax._` allow to compile code +// Removal of `import odinSyntax.LoggerSyntax` and remaining `catsSyntax` would fail to compile the `def fails` + +trait Foo[A] +trait Bar[A] + +trait WriterT[F[_]: Contravariant, L, V]: + def contramap[Z](fn: Z => V): WriterT[F, L, Z] = ??? +trait Logger[F[_]] +class WriterTLogger[F[_]] extends Logger[[G] =>> WriterT[F, List[String], G]] + +trait ContravariantMonoidal[F[_]] extends Invariant[F] with Contravariant[F] +trait Invariant[F[_]] +object Invariant: + given ContravariantMonoidal[Foo] = ??? + given ContravariantMonoidal[Bar] = ??? + +trait Contravariant[F[_]] extends Invariant[F] +object Contravariant: + trait Ops[F[_], A]: + def contramap[B](f: B => A): F[B] = ??? + +object catsSyntax: + implicit def toContravariantOps[F[_]: Contravariant, A](target: F[A]): Contravariant.Ops[F, A] = ??? + +object odinSyntax: + implicit class LoggerSyntax[F[_]](logger: Logger[F]): + def contramap(f: String => String): Logger[F] = ??? + +import catsSyntax._ +import odinSyntax.LoggerSyntax + +class Test: + def fails = new WriterTLogger[Option].contramap(identity) + def works = LoggerSyntax(new WriterTLogger[Option]).contramap(identity) + diff --git a/tests/pos/i18163.scala b/tests/pos/i18163.scala new file mode 100644 index 000000000000..5c364a50dd57 --- /dev/null +++ b/tests/pos/i18163.scala @@ -0,0 +1,21 @@ +import scala.language.implicitConversions + +trait Foo[A] +trait Bar[B] +trait Qux[C] +class Log[K[_]] + +trait Inv[F[_]] +object Inv: + given monFoo: Inv[Foo] = ??? + given monBar: Inv[Bar] = ??? + +trait InvOps[H[_], D] { def desc(s: String): H[D] = ??? } +trait LogOps[L[_]] { def desc(s: String): Log[L] = ??? } + +class Test: + implicit def LogOps[Q[_]](l: Log[Q]): LogOps[Q] = ??? + implicit def InvOps[J[_], E](j11: J[E])(implicit z: Inv[J]): InvOps[J, E] = ??? + + def fails = new Log[Qux].desc("fails") + def works = LogOps[Qux](new Log[Qux]).desc("works") From c569a4f4c691c8eaf5536cd90e3935553932b8fd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 1 Aug 2023 14:27:06 +0100 Subject: [PATCH 121/144] Space: Fix intersectUnrelatedAtomicTypes tracing --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 7238756454b3..002e3646c663 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -330,7 +330,7 @@ object SpaceEngine { * The types should be atomic (non-decomposable) and unrelated (neither * should be a subtype of the other). */ - def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) { + def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug, show) { // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1). if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then // Since projections of types don't include null, intersection with null is empty. From 518c02055f3addd2b4ea08ebaa6ac9c3ae65392e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 1 Aug 2023 14:47:31 +0100 Subject: [PATCH 122/144] Space: Make isDecomposableToChildren ignore type constructors --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 002e3646c663..467b36df3805 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -642,7 +642,7 @@ object SpaceEngine { // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. parts.map(tp.derivedAppliedType(_, targs)) - case tp if tp.classSymbol.isDecomposableToChildren => + case tp if tp.isDecomposableToChildren => def getChildren(sym: Symbol): List[Symbol] = sym.children.flatMap { child => if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz... @@ -678,8 +678,8 @@ object SpaceEngine { rec(tp, Nil) } - extension (cls: Symbol) - /** A type is decomposable to children if it's sealed, + extension (tp: Type) + /** A type is decomposable to children if it has a simple kind, it's sealed, * abstract (or a trait) - so its not a sealed concrete class that can be instantiated on its own, * has no anonymous children, which we wouldn't be able to name as counter-examples, * but does have children. @@ -688,7 +688,8 @@ object SpaceEngine { * A sealed trait with subclasses that then get removed after `refineUsingParent`, decomposes to the empty list. * So that's why we consider whether a type has children. */ def isDecomposableToChildren(using Context): Boolean = - cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty + val cls = tp.classSymbol + tp.hasSimpleKind && cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) From 86782076c45e88fc16c3abb0ca8646ce4a2dd417 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 1 Aug 2023 16:00:31 +0100 Subject: [PATCH 123/144] Space: Revert how invariant targs are erased to fix regression The motivating case (i16451) is complicated, because it involves unchecked type arguments. To fix the regression, I'm reverting the fix. --- .../tools/dotc/transform/patmat/Space.scala | 13 ++---------- .../suppressed-type-test-warnings.scala | 2 ++ .../isInstanceOf/enum-approx2.scala | 2 ++ .../neg-custom-args/isInstanceOf/i11178.scala | 1 + .../neg-custom-args/isInstanceOf/i8932.scala | 1 + tests/{ => pending}/neg/i16451.check | 0 tests/{ => pending}/neg/i16451.scala | 4 ---- tests/pos/i17230.bootstrap.scala | 16 +++++++++++++++ tests/pos/i17230.min1.scala | 15 ++++++++++++++ tests/pos/i17230.orig.scala | 20 +++++++++++++++++++ 10 files changed, 59 insertions(+), 15 deletions(-) rename tests/{ => pending}/neg/i16451.check (100%) rename tests/{ => pending}/neg/i16451.scala (93%) create mode 100644 tests/pos/i17230.bootstrap.scala create mode 100644 tests/pos/i17230.min1.scala create mode 100644 tests/pos/i17230.orig.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 467b36df3805..eab65890c227 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -468,17 +468,8 @@ object SpaceEngine { WildcardType case tp @ AppliedType(tycon, args) => - val args2 = - if tycon.isRef(defn.ArrayClass) then - args.map(arg => erase(arg, inArray = true, isValue = false)) - else tycon.typeParams.lazyZip(args).map { (tparam, arg) => - if isValue && tparam.paramVarianceSign == 0 then - // when matching against a value, - // any type argument for an invariant type parameter will be unchecked, - // meaning it won't fail to match against anything; thus the wildcard replacement - WildcardType - else erase(arg, inArray = false, isValue = false) - } + val inArray = tycon.isRef(defn.ArrayClass) + val args2 = args.map(arg => erase(arg, inArray = inArray, isValue = false)) tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) case tp @ OrType(tp1, tp2) => diff --git a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala index 175096fc6b21..92d86b3307e5 100644 --- a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala +++ b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala @@ -18,10 +18,12 @@ object Test { def err2[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[B] => // spurious // error b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } def fail[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[Int] => // error b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } } diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala index 5e3bdef7553d..c7c8a6c4e1fb 100644 --- a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -4,5 +4,7 @@ case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] class Test { def eval(e: Fun[Int, Int]) = e match { case Fun(x: Fun[Int, Double]) => ??? // error + case Fun(x: Exp[Int => String]) => ??? // error + case _ => } } diff --git a/tests/neg-custom-args/isInstanceOf/i11178.scala b/tests/neg-custom-args/isInstanceOf/i11178.scala index 71bc346e5743..47e8b4c3acab 100644 --- a/tests/neg-custom-args/isInstanceOf/i11178.scala +++ b/tests/neg-custom-args/isInstanceOf/i11178.scala @@ -12,6 +12,7 @@ object Test1 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // error + case _ => ??? } } diff --git a/tests/neg-custom-args/isInstanceOf/i8932.scala b/tests/neg-custom-args/isInstanceOf/i8932.scala index e070fdae518c..84d2f7d4990a 100644 --- a/tests/neg-custom-args/isInstanceOf/i8932.scala +++ b/tests/neg-custom-args/isInstanceOf/i8932.scala @@ -6,6 +6,7 @@ class Dummy extends Bar[Nothing] with Foo[String] def bugReport[A](foo: Foo[A]): Foo[A] = foo match { case bar: Bar[A] => bar // error + case dummy: Dummy => ??? } def test = bugReport(new Dummy: Foo[String]) diff --git a/tests/neg/i16451.check b/tests/pending/neg/i16451.check similarity index 100% rename from tests/neg/i16451.check rename to tests/pending/neg/i16451.check diff --git a/tests/neg/i16451.scala b/tests/pending/neg/i16451.scala similarity index 93% rename from tests/neg/i16451.scala rename to tests/pending/neg/i16451.scala index 685b79477bbe..49997d2bcf92 100644 --- a/tests/neg/i16451.scala +++ b/tests/pending/neg/i16451.scala @@ -1,10 +1,6 @@ // scalac: -Werror enum Color: case Red, Green -//sealed trait Color -//object Color: -// case object Red extends Color -// case object Green extends Color case class Wrapper[A](value: A) diff --git a/tests/pos/i17230.bootstrap.scala b/tests/pos/i17230.bootstrap.scala new file mode 100644 index 000000000000..ef2d98d8f55b --- /dev/null +++ b/tests/pos/i17230.bootstrap.scala @@ -0,0 +1,16 @@ +type Untyped = Type | Null + +class Type +abstract class SearchFailureType extends Type + +abstract class Tree[+T <: Untyped]: + def tpe: T = null.asInstanceOf[T] + +class SearchFailureIdent[+T <: Untyped] extends Tree[T] + +class Test_i17230_bootstrap: + def t1(arg: Tree[Type]) = arg match + case arg: SearchFailureIdent[?] => arg.tpe match + case x: SearchFailureType => + case _ => + case _ => diff --git a/tests/pos/i17230.min1.scala b/tests/pos/i17230.min1.scala new file mode 100644 index 000000000000..e2df63e168c1 --- /dev/null +++ b/tests/pos/i17230.min1.scala @@ -0,0 +1,15 @@ +// scalac: -Werror +trait Foo: + type Bar[_] + +object Foo: + type Aux[B[_]] = Foo { type Bar[A] = B[A] } + +class Test: + def t1[B[_]](self: Option[Foo.Aux[B]]) = self match + case Some(_) => 1 + case None => 2 + + def t2[B[_]](self: Option[Foo.Aux[B]]) = self match + case Some(f) => 1 + case None => 2 diff --git a/tests/pos/i17230.orig.scala b/tests/pos/i17230.orig.scala new file mode 100644 index 000000000000..d72a0082a116 --- /dev/null +++ b/tests/pos/i17230.orig.scala @@ -0,0 +1,20 @@ +// scalac: -Werror +import scala.util.* + +trait Transaction { + type State[_] +} +object Transaction { + type of[S[_]] = Transaction { type State[A] = S[A] } +} +trait DynamicScope[State[_]] + +case class ScopeSearch[State[_]](self: Either[Transaction.of[State], DynamicScope[State]]) { + + def embedTransaction[T](f: Transaction.of[State] => T): T = + self match { + case Left(integrated) => ??? + case Right(ds) => ??? + } +} + From 5d6891fe3de921a825d53b369cc9b3e805275753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 10 Aug 2023 09:17:12 +0200 Subject: [PATCH 124/144] Add changelog for 3.3.1-RC5 --- changelogs/3.3.1-RC5.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 changelogs/3.3.1-RC5.md diff --git a/changelogs/3.3.1-RC5.md b/changelogs/3.3.1-RC5.md new file mode 100644 index 000000000000..e0bfc2a7fea8 --- /dev/null +++ b/changelogs/3.3.1-RC5.md @@ -0,0 +1,22 @@ +# Backported fixes + +- Heal stage inconsistent prefixes of type projections [#18239](https://github.com/lampepfl/dotty/pull/18239) +- Fix regression #17245: Overloaded methods with ClassTags [#18286](http://github.com/lampepfl/dotty/pull/18286) +- Disallow taking singleton types of packages again [#18232](http://github.com/lampepfl/dotty/pull/18232) +- A slightly more conservative version of #14218 [#18352](http://github.com/lampepfl/dotty/pull/18352) +- Record failures to adapt application arguments [#18269](http://github.com/lampepfl/dotty/pull/18269) +- Fix regression in exhaustivity of HK types [#18303](http://github.com/lampepfl/dotty/pull/18303) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC4..3.3.1-RC5` these are: + +``` + 5 Dale Wijnand + 2 Martin Odersky + 2 PaweƂ Marks + 1 Jan Chyb + 1 Nicolas Stucki +``` From 059748245f9e0816a8f9d837b0b2625956853aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 10 Aug 2023 09:19:25 +0200 Subject: [PATCH 125/144] Release 3.3.1-RC5 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index a60932eb9e30..b1c6e63cef9d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC4" + val baseVersion = "3.3.1-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.1-RC3" + val previousDottyVersion = "3.3.1-RC4" object CompatMode { final val BinaryCompatible = 0 From 8e9b7182f41d03f412b2f2db0c8414f1411aa70c Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 13 Jul 2023 20:42:43 +0200 Subject: [PATCH 126/144] Refine infoDependsOnPrefix infoDependsOnPrefix now also considers non-final term members. Before 8d65f19 it only considered abstract types. Constructors were classified as non-final, which caused regression. We now exclude constructors specifically. Maybe we should instead classify them as effectively final. Fixes #18160 --- .../src/dotty/tools/dotc/core/Types.scala | 1 + .../tools/dotc/transform/TreeChecker.scala | 2 +- tests/pos/i18160/Test_2.scala | 11 ++++++++ tests/pos/i18160/repro_1.scala | 25 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18160/Test_2.scala create mode 100644 tests/pos/i18160/repro_1.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index bb4fd02816a6..81fc28a32fec 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2537,6 +2537,7 @@ object Types { (symd.isAbstractType || symd.isTerm && !symd.flagsUNSAFE.isOneOf(Module | Final | Param) + && !symd.isConstructor && !symd.maybeOwner.isEffectivelyFinal) && prefix.sameThis(symd.maybeOwner.thisType) && refines(givenSelfTypeOrCompleter(prefix.cls), symd.name) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index e50fb9d8b09c..34b3183a6b15 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -544,7 +544,7 @@ object TreeChecker { val TypeDef(_, impl @ Template(constr, _, _, _)) = cdef: @unchecked assert(cdef.symbol == cls) assert(impl.symbol.owner == cls) - assert(constr.symbol.owner == cls) + assert(constr.symbol.owner == cls, i"constr ${constr.symbol} in $cdef has wrong owner; should be $cls but is ${constr.symbol.owner}") assert(cls.primaryConstructor == constr.symbol, i"mismatch, primary constructor ${cls.primaryConstructor}, in tree = ${constr.symbol}") checkOwner(impl) checkOwner(impl.constr) diff --git a/tests/pos/i18160/Test_2.scala b/tests/pos/i18160/Test_2.scala new file mode 100644 index 000000000000..9ee40c3d37f9 --- /dev/null +++ b/tests/pos/i18160/Test_2.scala @@ -0,0 +1,11 @@ +class SynchronizedReevaluation +class SynchronizedReevaluationApi[Api <: RescalaInterface](val api: Api){ + import api._ + + def SynchronizedReevaluation[A](evt: Event[A])(implicit + turnSource: CreationTicket + ): (SynchronizedReevaluation, Event[A]) = { + val sync = new SynchronizedReevaluation + (sync, evt.map(identity)(turnSource)) + } +} diff --git a/tests/pos/i18160/repro_1.scala b/tests/pos/i18160/repro_1.scala new file mode 100644 index 000000000000..060f2d325d2d --- /dev/null +++ b/tests/pos/i18160/repro_1.scala @@ -0,0 +1,25 @@ +object core { + final class CreationTicket[State[_]] +} + +trait ReadAs[S[_], +A] { type State[V] = S[V] } + +trait EventCompatBundle { + bundle: Operators => + + trait EventCompat[+T] extends ReadAs[State, Option[T]] { + selfType: Event[T] => + final inline def map[B](inline expression: T => B)(implicit ticket: CreationTicket): Event[B] = ??? + } +} + +trait EventBundle extends EventCompatBundle { self: Operators => + trait Event[+T] extends EventCompat[T]: + final override type State[V] = self.State[V] +} +trait Operators extends EventBundle { + type State[_] + type CreationTicket = core.CreationTicket[State] +} +trait RescalaInterface extends Operators + From d2a0b3cd298ec45ef34942b23829f305cfca6d96 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 13 Jul 2023 20:53:37 +0200 Subject: [PATCH 127/144] Make constructors effectively final This is mainly a cleanup. --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 1 + compiler/src/dotty/tools/dotc/transform/init/Util.scala | 6 ++---- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 988a37be4388..b8c17ff61e9e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1196,6 +1196,7 @@ object SymDenotations { isOneOf(EffectivelyFinalFlags) || is(Inline, butNot = Deferred) || is(JavaDefinedVal, butNot = Method) + || isConstructor || !owner.isExtensibleClass /** A class is effectively sealed if has the `final` or `sealed` modifier, or it diff --git a/compiler/src/dotty/tools/dotc/transform/init/Util.scala b/compiler/src/dotty/tools/dotc/transform/init/Util.scala index 4e60c1325b09..ba2216504aef 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Util.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Util.scala @@ -75,11 +75,9 @@ object Util: case _ => None - def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show) { - if (sym.isEffectivelyFinal || sym.isConstructor) sym + def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show): + if sym.isEffectivelyFinal then sym else sym.matchingMember(cls.appliedRef) - } - extension (sym: Symbol) def hasSource(using Context): Boolean = !sym.defTree.isEmpty diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index fe28a8b18833..025eae3606af 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1689,7 +1689,7 @@ class RefChecks extends MiniPhase { thisPhase => // if (settings.warnNullaryUnit) // checkNullaryMethodReturnType(sym) // if (settings.warnInaccessible) { - // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic) + // if (!sym.isEffectivelyFinal && !sym.isSynthetic) // checkAccessibilityOfReferencedTypes(tree) // } // tree match { From 0305d8878a7dbbbc0ff398cdec0fc079f067280f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Jul 2023 15:29:29 +0200 Subject: [PATCH 128/144] Do not compute `protoFormal` if `param.tpt` is empty This was accidentally moved before of the `if (!param.tpt.isEmpty)` guard in https://github.com/lampepfl/dotty/commit/0f7c3abc3706b2054c48f3b16991741edb3a4610#diff-8c9ece1772bd78160fc1c31e988664586c9df566a1d22ff99ef99dd6d5627a90R1534 Fixes #18276 --- .../src/dotty/tools/dotc/typer/Typer.scala | 49 +++++++++---------- tests/pos/i18276a.scala | 15 ++++++ tests/pos/i18276b.scala | 9 ++++ 3 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 tests/pos/i18276a.scala create mode 100644 tests/pos/i18276b.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb23262d1410..74be1dee9a9b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1596,32 +1596,31 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if desugared.isEmpty then val inferredParams: List[untpd.ValDef] = for ((param, i) <- params.zipWithIndex) yield - val (formalBounds, isErased) = protoFormal(i) - val param0 = - if (!param.tpt.isEmpty) param - else - val formal = formalBounds.loBound - val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing - val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) - // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, - // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) - val paramType = - // Strip inferred erased annotation, to avoid accidentally inferring erasedness - val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal - if knownFormal && !isBottomFromWildcard then - formal0 - else - inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse( - if knownFormal then formal0 - else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) - ) - val paramTpt = untpd.TypedSplice( - (if knownFormal then InferredTypeTree() else untpd.TypeTree()) - .withType(paramType.translateFromRepeated(toArray = false)) - .withSpan(param.span.endPos) + if (!param.tpt.isEmpty) param + else + val (formalBounds, isErased) = protoFormal(i) + val formal = formalBounds.loBound + val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing + val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) + // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, + // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) + val paramType = + // Strip inferred erased annotation, to avoid accidentally inferring erasedness + val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal + if knownFormal && !isBottomFromWildcard then + formal0 + else + inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse( + if knownFormal then formal0 + else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) ) - cpy.ValDef(param)(tpt = paramTpt) - if isErased then param0.withAddedFlags(Flags.Erased) else param0 + val paramTpt = untpd.TypedSplice( + (if knownFormal then InferredTypeTree() else untpd.TypeTree()) + .withType(paramType.translateFromRepeated(toArray = false)) + .withSpan(param.span.endPos) + ) + val param0 = cpy.ValDef(param)(tpt = paramTpt) + if isErased then param0.withAddedFlags(Flags.Erased) else param0 desugared = desugar.makeClosure(inferredParams, fnBody, resultTpt, isContextual, tree.span) typed(desugared, pt) diff --git a/tests/pos/i18276a.scala b/tests/pos/i18276a.scala new file mode 100644 index 000000000000..46c2722fd8be --- /dev/null +++ b/tests/pos/i18276a.scala @@ -0,0 +1,15 @@ +import scala.language.implicitConversions + +case class Assign(left: String, right: String) +class SyntaxAnalyser extends ParsersBase { + val x: Parser[String ~ String] = ??? + val y: Parser[Assign] = x.map(Assign.apply) +} + +class ParsersBase { + trait ~[+T, +U] + abstract class Parser[+T]: + def map[U](f: T => U): Parser[U] = ??? + + given [A, B, X]: Conversion[(A, B) => X, (A ~ B) => X] = ??? +} diff --git a/tests/pos/i18276b.scala b/tests/pos/i18276b.scala new file mode 100644 index 000000000000..a4d905293472 --- /dev/null +++ b/tests/pos/i18276b.scala @@ -0,0 +1,9 @@ +import scala.language.implicitConversions + +def foo(a: Int): Int = ??? +def bar(f: () => Int): Int = ??? + +given f: Conversion[Int => Int, () => Int] = ??? + +def test1: Int = bar(foo) // implicit conversion applied to foo +def test2: Int = bar(f(foo)) From 6bf8ac95d677bda402cb5ef44f15400b82481e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 21 Aug 2023 17:20:00 +0200 Subject: [PATCH 129/144] Revert "Normalize match type usage during implicit lookup" This reverts commit 5bafff7cc96f1f31f6e77620ca509dfa55d816b4. --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../dotty/tools/dotc/core/TypeErrors.scala | 3 --- .../dotty/tools/dotc/typer/Implicits.scala | 7 ------ tests/pos/i17395.scala | 25 ------------------- 4 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 tests/pos/i17395.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b84af998ffb6..6857e3da38ed 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3180,7 +3180,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { tp case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) - throw MatchTypeReductionError(em"Match type reduction $casesText") + throw TypeError(em"Match type reduction $casesText") inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index f59bd08da779..24a207da6836 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -46,9 +46,6 @@ object TypeError: def toMessage(using Context) = msg end TypeError -class MatchTypeReductionError(msg: Message)(using Context) extends TypeError: - def toMessage(using Context) = msg - class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 66f400d7eae0..4bbd6ee080b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -636,13 +636,6 @@ trait ImplicitRunInfo: case t: TypeLambda => for p <- t.paramRefs do partSeen += p traverseChildren(t) - case t: MatchType => - traverseChildren(t) - traverse(try t.normalized catch case _: MatchTypeReductionError => t) - case MatchType.InDisguise(mt) - if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map) - => - traverse(mt) case t => traverseChildren(t) diff --git a/tests/pos/i17395.scala b/tests/pos/i17395.scala deleted file mode 100644 index 87c0a45a9ff5..000000000000 --- a/tests/pos/i17395.scala +++ /dev/null @@ -1,25 +0,0 @@ -trait TC[T] - -object TC { - def optionTCForPart[T](implicit tc: TC[ExtractPart[T]]): TC[Option[ExtractPart[T]]] = new TC[Option[ExtractPart[T]]] {} -} - -type ExtractPart[T] = T match { - case PartField[t] => t -} -type PartField[T] = Any { type Part = T } - -class ValuePartHolder { - type Part = Value -} - -class Value -object Value { - implicit val tcValue: TC[Value] = new {} -} - -@main def main(): Unit = { -// import Value.tcValue // explicit import works around the issue, but shouldn't be necessary - val tc = TC.optionTCForPart[ValuePartHolder] - println(tc) -} From 340303fb983405cd6b123458f3573b3cf1505c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 22 Aug 2023 13:18:45 +0200 Subject: [PATCH 130/144] Add changelog for 3.3.1-RC6 --- changelogs/3.3.1-RC6.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 changelogs/3.3.1-RC6.md diff --git a/changelogs/3.3.1-RC6.md b/changelogs/3.3.1-RC6.md new file mode 100644 index 000000000000..f74ab7fe7e18 --- /dev/null +++ b/changelogs/3.3.1-RC6.md @@ -0,0 +1,17 @@ +# Backported fixes + +- Refine `infoDependsOnPrefix` [#18204](https://github.com/lampepfl/dotty/pull/18204) +- FDo not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) +- Revert "Normalize match type usage during implicit lookup" [#18440](http://github.com/lampepfl/dotty/pull/18440) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC4..3.3.1-RC5` these are: + +``` + 3 PaweƂ Marks + 2 Martin Odersky + 1 Nicolas Stucki +``` From 5f8485e13b66b6d64b8e52161b5de10699900543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 22 Aug 2023 13:14:01 +0200 Subject: [PATCH 131/144] Release 3.3.1-RC6 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index b1c6e63cef9d..047310df0a6b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC5" + val baseVersion = "3.3.1-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.1-RC4" + val previousDottyVersion = "3.3.1-RC5" object CompatMode { final val BinaryCompatible = 0 From 88e6725df9fc73d9894d8ae8205a54ca8d47851d Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 28 Aug 2023 16:55:10 +0200 Subject: [PATCH 132/144] Tweak selection from self types Previously, we rejected the case where a symbol of a self type selection was private if was not of the enclosing class. But that symbol could shadow a non-private symbol in a base class, so have to treat that case as well. Fixes #18631 --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 ++++++- tests/pos/i18361.scala | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18361.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 81fc28a32fec..fb66d133c0ba 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -808,9 +808,14 @@ object Types { // is made to save execution time in the common case. See i9844.scala for test cases. def qualifies(sd: SingleDenotation) = !sd.symbol.is(Private) || sd.symbol.owner == tp.cls - d match + d.match case d: SingleDenotation => if qualifies(d) then d else NoDenotation case d => d.filterWithPredicate(qualifies) + .orElse: + // Only inaccessible private symbols were found. But there could still be + // shadowed non-private symbols, so as a fallback search for those. + // Test case is i18361.scala. + findMember(name, pre, required, excluded | Private) else d else // There is a special case to handle: diff --git a/tests/pos/i18361.scala b/tests/pos/i18361.scala new file mode 100644 index 000000000000..a84d5f0a09db --- /dev/null +++ b/tests/pos/i18361.scala @@ -0,0 +1,15 @@ +package test1: + class Service(val name: String) + class CrudService(name: String) extends Service(name) + + trait Foo { self: CrudService => + val x = self.name + } + +package test2: + abstract class Service[F[_]](val name: String) + abstract class CrudService[F[_]](name: String) extends Service[F](name) + + trait Foo[F[_]] { self: CrudService[?] => + val x = self.name + } From fb6545872968e90798c5a411e69b79670df8e0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 29 Aug 2023 11:34:58 +0200 Subject: [PATCH 133/144] Revert "Add reflect `defn.FunctionClass` overloads" This reverts commit 9571b42a2ff63e897cb566c09259f5f8d1e7a021. --- .../quoted/runtime/impl/QuotesImpl.scala | 4 --- library/src/scala/quoted/Quotes.scala | 19 -------------- .../stdlibExperimentalDefinitions.scala | 2 -- tests/run-macros/tasty-definitions-1.check | 26 ------------------- .../tasty-definitions-1/quoted_1.scala | 1 - 5 files changed, 52 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index e178dc81b1a3..db4e3e6c6a05 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2811,10 +2811,6 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler if isErased then throw new Exception("Erased function classes are not supported. Use a refined `scala.runtime.ErasedFunction`") else dotc.core.Symbols.defn.FunctionSymbol(arity, isImplicit) - def FunctionClass(arity: Int): Symbol = - FunctionClass(arity, false, false) - def FunctionClass(arity: Int, isContextual: Boolean): Symbol = - FunctionClass(arity, isContextual, false) def ErasedFunctionClass = dotc.core.Symbols.defn.ErasedFunctionClass def TupleClass(arity: Int): Symbol = dotc.core.Symbols.defn.TupleType(arity).nn.classSymbol.asClass diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index c7d5719b0e1f..b6e5a12da2d8 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4295,27 +4295,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * - ... * - Nth element is `FunctionN` */ - // TODO: deprecate in 3.4 and stabilize FunctionClass(Int)/FunctionClass(Int,Boolean) - // @deprecated("Use overload of `FunctionClass` with 1 or 2 arguments","3.4") def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol - /** Class symbol of a function class `scala.FunctionN`. - * - * @param arity the arity of the function where `0 <= arity` - * @return class symbol of `scala.FunctionN` where `N == arity` - */ - @experimental - def FunctionClass(arity: Int): Symbol - - /** Class symbol of a context function class `scala.FunctionN` or `scala.ContextFunctionN`. - * - * @param arity the arity of the function where `0 <= arity` - * @param isContextual if it is a `scala.ContextFunctionN` - * @return class symbol of `scala.FunctionN` or `scala.ContextFunctionN` where `N == arity` - */ - @experimental - def FunctionClass(arity: Int, isContextual: Boolean): Symbol - /** The `scala.runtime.ErasedFunction` built-in trait. */ @experimental def ErasedFunctionClass: Symbol diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 644efb54c32e..5ccdb753e9b3 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -63,8 +63,6 @@ val experimentalDefinitionInLibrary = Set( "scala.annotation.MacroAnnotation", //// New APIs: Quotes - // Should be stabilized in 3.4.0 - "scala.quoted.Quotes.reflectModule.defnModule.FunctionClass", "scala.quoted.Quotes.reflectModule.FlagsModule.AbsOverride", // Can be stabilized in 3.4.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", diff --git a/tests/run-macros/tasty-definitions-1.check b/tests/run-macros/tasty-definitions-1.check index ce7251d7d3ee..4ac0e6267028 100644 --- a/tests/run-macros/tasty-definitions-1.check +++ b/tests/run-macros/tasty-definitions-1.check @@ -57,57 +57,31 @@ Function23 Function24 Function25 ContextFunction0 -ContextFunction0 -ContextFunction1 ContextFunction1 ContextFunction2 -ContextFunction2 -ContextFunction3 ContextFunction3 ContextFunction4 -ContextFunction4 -ContextFunction5 ContextFunction5 ContextFunction6 -ContextFunction6 ContextFunction7 -ContextFunction7 -ContextFunction8 ContextFunction8 ContextFunction9 -ContextFunction9 ContextFunction10 -ContextFunction10 -ContextFunction11 ContextFunction11 ContextFunction12 -ContextFunction12 ContextFunction13 -ContextFunction13 -ContextFunction14 ContextFunction14 ContextFunction15 -ContextFunction15 -ContextFunction16 ContextFunction16 ContextFunction17 -ContextFunction17 -ContextFunction18 ContextFunction18 ContextFunction19 -ContextFunction19 ContextFunction20 -ContextFunction20 -ContextFunction21 ContextFunction21 ContextFunction22 -ContextFunction22 ContextFunction23 -ContextFunction23 -ContextFunction24 ContextFunction24 ContextFunction25 -ContextFunction25 class java.lang.Exception: Erased function classes are not supported. Use a refined `scala.runtime.ErasedFunction` ErasedFunction Tuple2 diff --git a/tests/run-macros/tasty-definitions-1/quoted_1.scala b/tests/run-macros/tasty-definitions-1/quoted_1.scala index bf9e28288486..ed210706f567 100644 --- a/tests/run-macros/tasty-definitions-1/quoted_1.scala +++ b/tests/run-macros/tasty-definitions-1/quoted_1.scala @@ -60,7 +60,6 @@ object Macros { printout(defn.FunctionClass(i).name) for (i <- 0 to 25) - printout(defn.FunctionClass(i, isContextual = true).name) printout(defn.FunctionClass(i, isImplicit = true).name) // should fail From 24cd50d2d66caeb0930032d3e23bb1a60fe02528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 29 Aug 2023 17:56:55 +0200 Subject: [PATCH 134/144] Add changelog for 3.3.1-RC7 --- changelogs/3.3.1-RC7.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 changelogs/3.3.1-RC7.md diff --git a/changelogs/3.3.1-RC7.md b/changelogs/3.3.1-RC7.md new file mode 100644 index 000000000000..f8f093a18d11 --- /dev/null +++ b/changelogs/3.3.1-RC7.md @@ -0,0 +1,16 @@ +# Backported fixes + +- Tweak selection from self types [#18467](https://github.com/lampepfl/dotty/pull/18467) +- Revert "Add reflect `defn.FunctionClass` overloads" [#18473](http://github.com/lampepfl/dotty/pull/18473) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC6..3.3.1-RC7` these are: + +``` + 3 PaweƂ Marks + 1 Martin Odersky + +``` From ca005766359c27ea06adc18409b6e53f79732bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 29 Aug 2023 17:58:04 +0200 Subject: [PATCH 135/144] Release 3.3.1-RC7 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 047310df0a6b..f748ce44d4ca 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC6" + val baseVersion = "3.3.1-RC7" // 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.1-RC5" + val previousDottyVersion = "3.3.1-RC6" object CompatMode { final val BinaryCompatible = 0 From 9b4ea8eb22dd96c94eb5d5c62cd0c0d277342a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 5 Sep 2023 14:33:24 +0200 Subject: [PATCH 136/144] Add changelog for 3.3.1 Also fix typos in older changelogs --- changelogs/3.3.1-RC1.md | 15 ++- changelogs/3.3.1-RC6.md | 4 +- changelogs/3.3.1.md | 287 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 changelogs/3.3.1.md diff --git a/changelogs/3.3.1-RC1.md b/changelogs/3.3.1-RC1.md index 4e52eb874891..e7d9f8f87ea9 100644 --- a/changelogs/3.3.1-RC1.md +++ b/changelogs/3.3.1-RC1.md @@ -42,6 +42,7 @@ - Harden tpd.Apply/TypeApply in case of errors [#16887](https://github.com/lampepfl/dotty/pull/16887) - Try to be more subtle when inferring type parameters of class parents [#16896](https://github.com/lampepfl/dotty/pull/16896) - Include `P` in the implicit scope of `P.this.type` [#17088](https://github.com/lampepfl/dotty/pull/17088) +- Do not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) ## Incremental Compilation @@ -71,7 +72,6 @@ ## Match Types -- Normalize match type usage during implicit lookup [#17457](https://github.com/lampepfl/dotty/pull/17457) - Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. [#17322](https://github.com/lampepfl/dotty/pull/17322) - Fix match type reduction with wildcard type arguments [#17065](https://github.com/lampepfl/dotty/pull/17065) - Fix check whether classtag can be generated for match types [#16708](https://github.com/lampepfl/dotty/pull/16708) @@ -86,6 +86,7 @@ - Check outer class prefixes in type projections when pattern matching [#17136](https://github.com/lampepfl/dotty/pull/17136) - Make unchecked cases non-`@unchecked` and non-unreachable [#16958](https://github.com/lampepfl/dotty/pull/16958) - Fix #16899: Better handle X instanceOf P where X is T1 | T2 [#17382](https://github.com/lampepfl/dotty/pull/17382) +- Fix regression in exhaustivity of HK types [#18303](http://github.com/lampepfl/dotty/pull/18303) ## Pickling @@ -121,6 +122,7 @@ - Only transform the body of the quote with QuoteTransformer [#17451](https://github.com/lampepfl/dotty/pull/17451) - Place staged type captures in Quote AST [#17424](https://github.com/lampepfl/dotty/pull/17424) - Add SplicePattern AST to parse and type quote pattern splices [#17396](https://github.com/lampepfl/dotty/pull/17396) +- Dealias types in `New`` before matching quotes [#17615](https://github.com/lampepfl/dotty/pull/17615) ## Reflection @@ -129,7 +131,6 @@ - Fix reflect.LambdaType type test [#16972](https://github.com/lampepfl/dotty/pull/16972) - Improve `New`/`Select` -Ycheck message [#16746](https://github.com/lampepfl/dotty/pull/16746) - Improve error message for CyclicReference in macros [#16749](https://github.com/lampepfl/dotty/pull/16749) -- Add reflect `defn.FunctionClass` overloads [#16849](https://github.com/lampepfl/dotty/pull/16849) ## REPL @@ -222,6 +223,16 @@ - Fix #16405 ctd - wildcards prematurely resolving to Nothing [#16764](https://github.com/lampepfl/dotty/pull/16764) - Test: add regression test for #7790 [#17473](https://github.com/lampepfl/dotty/pull/17473) - Properly handle `AnyVal`s as refinement members of `Selectable`s [#16286](https://github.com/lampepfl/dotty/pull/16286) +- Fix `accessibleType` for package object prefixes [#18057](https://github.com/lampepfl/dotty/pull/18057) +- Add clause for protected visibility from package objects [#18134](https://github.com/lampepfl/dotty/pull/18134) +- Revert "Include top-level symbols from same file in outer ambiguity error" [#17438](https://github.com/lampepfl/dotty/pull/17438) +- Heal stage inconsistent prefixes of type projections [#18239](https://github.com/lampepfl/dotty/pull/18239) +- Fix regression #17245: Overloaded methods with ClassTags [#18286](http://github.com/lampepfl/dotty/pull/18286) +- Disallow taking singleton types of packages again [#18232](http://github.com/lampepfl/dotty/pull/18232) +- A slightly more conservative version of #14218 [#18352](http://github.com/lampepfl/dotty/pull/18352) +- Record failures to adapt application arguments [#18269](http://github.com/lampepfl/dotty/pull/18269) +- Refine `infoDependsOnPrefix` [#18204](httpsF://github.com/lampepfl/dotty/pull/18204) +- Tweak selection from self types [#18467](https://github.com/lampepfl/dotty/pull/18467) # Contributors diff --git a/changelogs/3.3.1-RC6.md b/changelogs/3.3.1-RC6.md index f74ab7fe7e18..96181855f1a0 100644 --- a/changelogs/3.3.1-RC6.md +++ b/changelogs/3.3.1-RC6.md @@ -1,14 +1,14 @@ # Backported fixes - Refine `infoDependsOnPrefix` [#18204](https://github.com/lampepfl/dotty/pull/18204) -- FDo not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) +- Do not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) - Revert "Normalize match type usage during implicit lookup" [#18440](http://github.com/lampepfl/dotty/pull/18440) # Contributors Thank you to all the contributors who made this release possible 🎉 -According to `git shortlog -sn --no-merges 3.3.1-RC4..3.3.1-RC5` these are: +According to `git shortlog -sn --no-merges 3.3.1-RC5..3.3.1-RC6` these are: ``` 3 PaweƂ Marks diff --git a/changelogs/3.3.1.md b/changelogs/3.3.1.md new file mode 100644 index 000000000000..5bbd6eb2861c --- /dev/null +++ b/changelogs/3.3.1.md @@ -0,0 +1,287 @@ +# Highlights of the release + +- Support records in JavaParsers [#16762](https://github.com/lampepfl/dotty/pull/16762) +- Port JVM backend refactor from Scala 2 [#15322](https://github.com/lampepfl/dotty/pull/15322) + +# Other changes and fixes + +## Backend + +- Disallow mixins where super calls bind to vals [#16908](https://github.com/lampepfl/dotty/pull/16908) +- Fix #15107: Avoid re-emitting a LineNumber after only LabelNodes. [#16813](https://github.com/lampepfl/dotty/pull/16813) + +## Coverage + +- Fix #17042: Preserve the shape of secondary ctors in instrumentCoverage. [#17111](https://github.com/lampepfl/dotty/pull/17111) + +## Default parameters + +- Dupe fix when finding default arg getters [#17058](https://github.com/lampepfl/dotty/pull/17058) + +## Documentation + +- Fix: ensure syntax blocks for ebnf are marked as such [#16837](https://github.com/lampepfl/dotty/pull/16837) + +## Erasure + +- Handle `@companionClass` and `@companionMethod` meta-annotations [#17091](https://github.com/lampepfl/dotty/pull/17091) + +## Extension Methods + +- Support extension methods imported from different objects [#17050](https://github.com/lampepfl/dotty/pull/17050) + +## GADTs + +- Fix tuple member selection so it works with GADT healing [#16766](https://github.com/lampepfl/dotty/pull/16766) +- Fix upper bound constraints, that are higher-kinded [#16744](https://github.com/lampepfl/dotty/pull/16744) +- Split out immutable GadtConstraint [#16602](https://github.com/lampepfl/dotty/pull/16602) + +## Implicits + +- Improve subtyping check for not yet eta-expanded higher kinded types [#17139](https://github.com/lampepfl/dotty/pull/17139) +- Harden tpd.Apply/TypeApply in case of errors [#16887](https://github.com/lampepfl/dotty/pull/16887) +- Try to be more subtle when inferring type parameters of class parents [#16896](https://github.com/lampepfl/dotty/pull/16896) +- Include `P` in the implicit scope of `P.this.type` [#17088](https://github.com/lampepfl/dotty/pull/17088) + +## Incremental Compilation + +- Fix under-compilation when the method type in a SAM changes [#16996](https://github.com/lampepfl/dotty/pull/16996) + +## Infrastructure + +- Set reference version to 3.3.0-RC6 [#17504](https://github.com/lampepfl/dotty/pull/17504) +- Fix #17119: Download Coursier from GitHub directly [#17141](https://github.com/lampepfl/dotty/pull/17141) + +## Inline + +- Remove NamedArg from inlined arguments [#17228](https://github.com/lampepfl/dotty/pull/17228) +- Don't generate a Select for a TermRef with NoPrefix [#16754](https://github.com/lampepfl/dotty/pull/16754) +- Prepare bodies of inline forwarders eagerly [#16757](https://github.com/lampepfl/dotty/pull/16757) +- Do not remove inline method implementations until PruneErasedDefs [#17408](https://github.com/lampepfl/dotty/pull/17408) + +## Java Interop + +- ClassfileParser: allow missing param names (for JDK 21) [#17536](https://github.com/lampepfl/dotty/pull/17536) + +## Linting + +- Improve -Wunused: locals, privates with unset vars warning #16639 [#17160](https://github.com/lampepfl/dotty/pull/17160) +- Fix wunused false positive when deriving alias type [#17157](https://github.com/lampepfl/dotty/pull/17157) +- Port `-Wnonunit-statement` setting for dotty [#16936](https://github.com/lampepfl/dotty/pull/16936) + +## Match Types + +- Normalize match type usage during implicit lookup [#17457](https://github.com/lampepfl/dotty/pull/17457) +- Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. [#17322](https://github.com/lampepfl/dotty/pull/17322) +- Fix match type reduction with wildcard type arguments [#17065](https://github.com/lampepfl/dotty/pull/17065) +- Fix check whether classtag can be generated for match types [#16708](https://github.com/lampepfl/dotty/pull/16708) + +## Parser + +- Allow lines starting with `.` to fall outside previous indentation widths [#17056](https://github.com/lampepfl/dotty/pull/17056) + +## Pattern Matching + +- Fix #11541: Specialize ClassTag[T] in exhaustivity check [#17385](https://github.com/lampepfl/dotty/pull/17385) +- Check outer class prefixes in type projections when pattern matching [#17136](https://github.com/lampepfl/dotty/pull/17136) +- Make unchecked cases non-`@unchecked` and non-unreachable [#16958](https://github.com/lampepfl/dotty/pull/16958) +- Fix #16899: Better handle X instanceOf P where X is T1 | T2 [#17382](https://github.com/lampepfl/dotty/pull/17382) + +## Pickling + +- ClassfileParser: Avoid cycle when accessing companion in inner class lookup [#16882](https://github.com/lampepfl/dotty/pull/16882) + +## Polyfunctions + +- Fix type aliases in beta-reduction of polyfunctions [#17054](https://github.com/lampepfl/dotty/pull/17054) + +## Quotes + +- Register `paramProxy` and `thisProxy` in `Quote` type [#17541](https://github.com/lampepfl/dotty/pull/17541) +- Only check newVal/newMethod privateWithin on -Xcheck-macros [#17437](https://github.com/lampepfl/dotty/pull/17437) +- Unencode quote and splice trees [#17342](https://github.com/lampepfl/dotty/pull/17342) +- Correctly type Expr.ofTupleFromSeq for arity > 22 [#17261](https://github.com/lampepfl/dotty/pull/17261) +- Use TermRef to distinguish distinct Type[T] instances [#17205](https://github.com/lampepfl/dotty/pull/17205) +- Check level consistency of SingletonTypeTree as a type [#17209](https://github.com/lampepfl/dotty/pull/17209) +- Fix splice type variable pattern detection [#17048](https://github.com/lampepfl/dotty/pull/17048) +- Avoid creation of `@SplicedType` quote local refrences [#17051](https://github.com/lampepfl/dotty/pull/17051) +- Dealias type references when healing types in quotes [#17049](https://github.com/lampepfl/dotty/pull/17049) +- Replace quoted type variables in signature of HOAS pattern result [#16951](https://github.com/lampepfl/dotty/pull/16951) +- Beta-reduce directly applied PolymorphicFunction [#16623](https://github.com/lampepfl/dotty/pull/16623) +- Use `Object.toString` for `quoted.{Expr, Type}` [#16663](https://github.com/lampepfl/dotty/pull/16663) +- Fix Splicer.isEscapedVariable [#16838](https://github.com/lampepfl/dotty/pull/16838) +- Fix references to class members defined in quotes [#17107](https://github.com/lampepfl/dotty/pull/17107) +- Handle pickled forward references in pickled expressions [#16855](https://github.com/lampepfl/dotty/pull/16855) +- Fix #16615 - crashes of path dependent types in spliced Type.of [#16773](https://github.com/lampepfl/dotty/pull/16773) +- Disallow local term references in staged types [#16362](https://github.com/lampepfl/dotty/pull/16362) +- Refactor level checking / type healing logic [#17082](https://github.com/lampepfl/dotty/pull/17082) +- Dealias quoted types when staging [#17059](https://github.com/lampepfl/dotty/pull/17059) +- Fix quotes with references to path dependent types [#17081](https://github.com/lampepfl/dotty/pull/17081) +- Make arguments order in quote hole deterministic [#17405](https://github.com/lampepfl/dotty/pull/17405) +- Only transform the body of the quote with QuoteTransformer [#17451](https://github.com/lampepfl/dotty/pull/17451) +- Place staged type captures in Quote AST [#17424](https://github.com/lampepfl/dotty/pull/17424) +- Add SplicePattern AST to parse and type quote pattern splices [#17396](https://github.com/lampepfl/dotty/pull/17396) + +## Reflection + +- -Xcheck-macros: add hint when a symbol in created twice [#16733](https://github.com/lampepfl/dotty/pull/16733) +- Assert that symbols created using reflect API have correct privateWithin symbols [#17352](https://github.com/lampepfl/dotty/pull/17352) +- Fix reflect.LambdaType type test [#16972](https://github.com/lampepfl/dotty/pull/16972) +- Improve `New`/`Select` -Ycheck message [#16746](https://github.com/lampepfl/dotty/pull/16746) +- Improve error message for CyclicReference in macros [#16749](https://github.com/lampepfl/dotty/pull/16749) +- Add reflect `defn.FunctionClass` overloads [#16849](https://github.com/lampepfl/dotty/pull/16849) + +## REPL + +- Always load REPL classes in macros including the output directory [#16866](https://github.com/lampepfl/dotty/pull/16866) + +## Reporting + +- Improve missing argument list error [#17126](https://github.com/lampepfl/dotty/pull/17126) +- Improve implicit parameter error message with aliases [#17125](https://github.com/lampepfl/dotty/pull/17125) +- Improve "constructor proxy shadows outer" handling [#17154](https://github.com/lampepfl/dotty/pull/17154) +- Clarify ambiguous reference error message [#16137](https://github.com/lampepfl/dotty/pull/16137) +- Hint about forbidden combination of implicit values and conversions [#16735](https://github.com/lampepfl/dotty/pull/16735) +- Attach explanation message to diagnostic message [#16787](https://github.com/lampepfl/dotty/pull/16787) +- Propagate implicit search errors from implicit macros [#16840](https://github.com/lampepfl/dotty/pull/16840) +- Detail UnapplyInvalidReturnType error message [#17167](https://github.com/lampepfl/dotty/pull/17167) +- Add way to debug -Xcheck-macros tree checking [#16973](https://github.com/lampepfl/dotty/pull/16973) +- Enrich and finesse compiler crash reporting [#17031](https://github.com/lampepfl/dotty/pull/17031) +- Allow @implicitNotFound messages as explanations [#16893](https://github.com/lampepfl/dotty/pull/16893) +- Include top-level symbols from same file in outer ambiguity error [#17033](https://github.com/lampepfl/dotty/pull/17033) +- Do not issue deprecation warnings when declaring deprecated case classes [#17165](https://github.com/lampepfl/dotty/pull/17165) + +## Scala-JS + +- Fix #17344: Make implicit references to this above dynamic imports explicit. [#17357](https://github.com/lampepfl/dotty/pull/17357) +- Fix #12621: Better error message for JS trait ctor param. [#16811](https://github.com/lampepfl/dotty/pull/16811) +- Fix #16801: Handle Closure's of s.r.FunctionXXL. [#16809](https://github.com/lampepfl/dotty/pull/16809) +- Fix #17549: Unify how Memoize and Constructors decide what fields need storing. [#17560](https://github.com/lampepfl/dotty/pull/17560) + +## Scaladoc + +- Feat: Add a blog configuration with yaml [#17214](https://github.com/lampepfl/dotty/pull/17214) +- Don't render the "$" for module [#17302](https://github.com/lampepfl/dotty/pull/17302) +- Fix: Add scrollbar to the sidebar [#17203](https://github.com/lampepfl/dotty/pull/17203) +- Scaladoc: fix crash when processing extends call [#17260](https://github.com/lampepfl/dotty/pull/17260) +- Fix: Modify the CSS so that the logo of the generated documentation is adaptive [#17172](https://github.com/lampepfl/dotty/pull/17172) +- Fix: Remove the duplicate parameter when generating the scaladoc. [#17097](https://github.com/lampepfl/dotty/pull/17097) +- Fix: padding top in mobile version [#17019](https://github.com/lampepfl/dotty/pull/17019) +- Fix: tap target of the menu in Mobile version [#17018](https://github.com/lampepfl/dotty/pull/17018) +- Scaladoc: Fix expand icon not changing on anchor link [#17053](https://github.com/lampepfl/dotty/pull/17053) +- Scaladoc: fix inkuire generation for PolyTypes [#17129](https://github.com/lampepfl/dotty/pull/17129) +- Re port scroll bar [#17463](https://github.com/lampepfl/dotty/pull/17463) +- Handle empty files and truncated YAML front matter [#17527](https://github.com/lampepfl/dotty/pull/17527) + +## SemanticDB + +- Make sure symbol exists before calling owner [#16860](https://github.com/lampepfl/dotty/pull/16860) +- Support LambdaType (convert from HKTypeLambda) [#16056](https://github.com/lampepfl/dotty/pull/16056) + +## Specification + +- Apply `class-shadowing.md` to the Spec [#16839](https://github.com/lampepfl/dotty/pull/16839) +- Adding base for future Spec into the compiler repo [#16825](https://github.com/lampepfl/dotty/pull/16825) + +## Standard Library + +- Optimization: avoid NotGiven allocations [#17090](https://github.com/lampepfl/dotty/pull/17090) + +## Tooling + +- Disable `ExtractSemanticDB` phase when writing to output directory defined as JAR. [#16790](https://github.com/lampepfl/dotty/pull/16790) +- Print owner of bind symbol with -Yprint-debug-owners [#16854](https://github.com/lampepfl/dotty/pull/16854) +- Small fixes to allow using Metals with scaladoc with sbt [#16816](https://github.com/lampepfl/dotty/pull/16816) + +## Transform + +- Move CrossVersionChecks before FirstTransform [#17301](https://github.com/lampepfl/dotty/pull/17301) +- Fix needsOuterIfReferenced [#17159](https://github.com/lampepfl/dotty/pull/17159) +- Drop incorrect super accessor in trait subclass [#17062](https://github.com/lampepfl/dotty/pull/17062) +- Generate toString only for synthetic companions of case classes [#16890](https://github.com/lampepfl/dotty/pull/16890) +- Check trait constructor for accessibility even if not called at Typer [#17094](https://github.com/lampepfl/dotty/pull/17094) +- Fix #17435: A simpler fix [#17436](https://github.com/lampepfl/dotty/pull/17436) + +## Typer + +- Preserve type bounds for inlined definitions in posttyper [#17190](https://github.com/lampepfl/dotty/pull/17190) +- Change logic to find members of recursive types [#17386](https://github.com/lampepfl/dotty/pull/17386) +- Recognize named arguments in isFunctionWithUnknownParamType [#17161](https://github.com/lampepfl/dotty/pull/17161) +- Better comparisons for type projections [#17092](https://github.com/lampepfl/dotty/pull/17092) +- Allow selectDynamic and applyDynamic to be extension methods [#17106](https://github.com/lampepfl/dotty/pull/17106) +- Fix use of accessibleFrom when finding default arg getters [#16977](https://github.com/lampepfl/dotty/pull/16977) +- Map class literal constant types [#16988](https://github.com/lampepfl/dotty/pull/16988) +- Always use adapted type in withDenotation [#16901](https://github.com/lampepfl/dotty/pull/16901) +- Restrict captureWildcards to only be used if needed [#16799](https://github.com/lampepfl/dotty/pull/16799) +- Don't capture wildcards if in closure or by-name [#16732](https://github.com/lampepfl/dotty/pull/16732) +- Infer: Don't minimise to Nothing if there's an upper bound [#16786](https://github.com/lampepfl/dotty/pull/16786) +- Perform Matchable check only if type test is needed [#16824](https://github.com/lampepfl/dotty/pull/16824) +- Don't eta expand unary varargs methods [#16892](https://github.com/lampepfl/dotty/pull/16892) +- Fix beta-reduction with `Nothing` and `null` args [#16938](https://github.com/lampepfl/dotty/pull/16938) +- Generate kind-correct wildcards when selecting from a wildcard [#17025](https://github.com/lampepfl/dotty/pull/17025) +- Fix #16405 ctd - wildcards prematurely resolving to Nothing [#16764](https://github.com/lampepfl/dotty/pull/16764) +- Test: add regression test for #7790 [#17473](https://github.com/lampepfl/dotty/pull/17473) +- Properly handle `AnyVal`s as refinement members of `Selectable`s [#16286](https://github.com/lampepfl/dotty/pull/16286) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0..3.3.1` these are: + +``` + 152 Nicolas Stucki + 73 Martin Odersky + 54 Dale Wijnand + 51 Szymon Rodziewicz + 49 Quentin Bernet + 38 Chris Kipp + 31 PaweƂ Marks + 19 David Hua + 18 Lucas + 18 ysthakur + 15 Fengyun Liu + 14 Guillaume Martres + 14 Jamie Thompson + 11 SĂ©bastien Doeraene + 9 TimothĂ©e Andres + 8 Kacper Korban + 7 Matt Bovel + 7 Som Snytt + 6 Julien Richard-Foy + 6 Lucas Leblanc + 5 MichaƂ PaƂka + 4 Anatolii Kmetiuk + 4 Guillaume Raffin + 4 Jan Chyb + 4 Paul Coral + 4 Wojciech Mazur + 4 Yichen Xu + 3 Decel + 2 Adrien Piquerez + 2 Arman Bilge + 2 Carl + 2 Florian3k + 2 Kenji Yoshida + 2 Michael Pilquist + 2 Natsu Kagami + 2 Seth Tisue + 2 Tomasz Godzik + 2 Vasil Vasilev + 2 Yadu Krishnan + 1 Bersier + 1 Flavio Brasil + 1 Jan-Pieter van den Heuvel + 1 Lukas Rytz + 1 Miles Yucht + 1 Mohammad Yousuf Minhaj Zia + 1 Ondra Pelech + 1 Philippus + 1 Rikito Taniguchi + 1 Simon R + 1 brandonspark + 1 github-actions[bot] + 1 liang3zy22 + 1 s.bazarsadaev + 1 Ɓukasz WroƄski +``` From 721e7c87ee95b811984b7b992728729d7094c4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 5 Sep 2023 14:35:21 +0200 Subject: [PATCH 137/144] Release 3.3.1 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index f748ce44d4ca..f3ec6bb54548 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC7" + val baseVersion = "3.3.1" // 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.1-RC6" + val previousDottyVersion = "3.3.0" object CompatMode { final val BinaryCompatible = 0 From 3eb354bc1e9a47c2e92ba709ef83cb898d60fe83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20D=C4=85browski?= Date: Sun, 1 Oct 2023 21:56:51 +0200 Subject: [PATCH 138/144] Fix open-classes.md --- docs/_docs/reference/other-new-features/open-classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/other-new-features/open-classes.md b/docs/_docs/reference/other-new-features/open-classes.md index 764c234df599..10af6ead669e 100644 --- a/docs/_docs/reference/other-new-features/open-classes.md +++ b/docs/_docs/reference/other-new-features/open-classes.md @@ -77,4 +77,4 @@ A class that is neither `abstract` nor `open` is similar to a `sealed` class: it ## Migration -`open` is a new modifier in Scala 3. To allow cross compilation between Scala 2.13 and Scala 3.0 without warnings, the feature warning for ad-hoc extensions is produced only under `-source future`. It will be produced by default from Scala 3.1 on. +`open` is a new modifier in Scala 3. To allow cross compilation between Scala 2.13 and Scala 3.0 without warnings, the feature warning for ad-hoc extensions is produced only under `-source future`. It will be produced by default [from Scala 3.4 on](https://github.com/lampepfl/dotty/issues/16334). From bb5be18e11b412bc7fe39d76f0b4785297f49b86 Mon Sep 17 00:00:00 2001 From: Bersier Date: Wed, 6 Dec 2023 08:38:22 -0500 Subject: [PATCH 139/144] Update package-objects.md Package objects are not deprecated in Scala 3.3.1 yet. --- docs/_docs/reference/dropped-features/package-objects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/dropped-features/package-objects.md b/docs/_docs/reference/dropped-features/package-objects.md index d8149e460bf5..9fe5bbd2de41 100644 --- a/docs/_docs/reference/dropped-features/package-objects.md +++ b/docs/_docs/reference/dropped-features/package-objects.md @@ -11,7 +11,7 @@ package object p { def b = ... } ``` -will be dropped. They are still available in Scala 3.0 and 3.1, but will be deprecated and removed afterwards. +will be dropped. They are still available, but will be deprecated and removed at some point in the future. Package objects are no longer needed since all kinds of definitions can now be written at the top-level. Example: ```scala From 074128b9f2d6bce066e7137042b19dd401353ecc Mon Sep 17 00:00:00 2001 From: Bersier Date: Thu, 28 Dec 2023 11:21:51 -0500 Subject: [PATCH 140/144] Update wildcards.md The documentation gives incorrect Scala versions for the transitions. --- .../reference/changed-features/wildcards.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/_docs/reference/changed-features/wildcards.md b/docs/_docs/reference/changed-features/wildcards.md index 0d3e13c3d7e0..ac7235770e36 100644 --- a/docs/_docs/reference/changed-features/wildcards.md +++ b/docs/_docs/reference/changed-features/wildcards.md @@ -4,7 +4,7 @@ title: Wildcard Arguments in Types nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/wildcards.html --- -The syntax of wildcard arguments in types has changed from `_` to `?`. Example: +The syntax of wildcard arguments in types is changing from `_` to `?`. Example: ```scala List[?] Map[? <: AnyRef, ? >: Null] @@ -14,8 +14,8 @@ Map[? <: AnyRef, ? >: Null] We would like to use the underscore syntax `_` to stand for an anonymous type parameter, aligning it with its meaning in value parameter lists. So, just as `f(_)` is a shorthand for the lambda `x => f(x)`, in the future `C[_]` will be a shorthand -for the type lambda `[X] =>> C[X]`. This makes higher-kinded types easier to use. It also removes the wart that, used as a type -parameter, `F[_]` means `F` is a type constructor whereas used as a type, `F[_]` means it is a wildcard (i.e. existential) type. +for the type lambda `[X] =>> C[X]`. This will make higher-kinded types easier to use. It will also remove the wart that, used as a type +parameter, `F[_]` means `F` is a type constructor, whereas used as a type, `F[_]` means it is a wildcard (i.e. existential) type. In the future, `F[_]` will mean the same thing, no matter where it is used. We pick `?` as a replacement syntax for wildcard types, since it aligns with @@ -28,11 +28,11 @@ compiler plugin still uses the reverse convention, with `?` meaning parameter pl A step-by-step migration is made possible with the following measures: - 1. In Scala 3.0, both `_` and `?` are legal names for wildcards. - 2. In Scala 3.1, `_` is deprecated in favor of `?` as a name for a wildcard. A `-rewrite` option is + 1. In earlier versions of Scala 3, both `_` and `?` are legal names for wildcards. + 2. In Scala 3.4, `_` will be deprecated in favor of `?` as a name for wildcards. A `-rewrite` option is available to rewrite one to the other. - 3. In Scala 3.2, the meaning of `_` changes from wildcard to placeholder for type parameter. - 4. The Scala 3.1 behavior is already available today under the `-source future` setting. + 3. At some later point in the future, the meaning of `_` will change from wildcard to placeholder for type parameters. + 4. Some deprecation warnings are already available under the `-source future` setting. To smooth the transition for codebases that use kind-projector, we adopt the following measures under the command line option `-Ykind-projector`: @@ -42,7 +42,7 @@ option `-Ykind-projector`: available to rewrite one to the other. 3. In Scala 3.3, `*` is removed again, and all type parameter placeholders will be expressed with `_`. -These rules make it possible to cross build between Scala 2 using the kind projector plugin and Scala 3.0 - 3.2 using the compiler option `-Ykind-projector`. +These rules make it possible to cross-build between Scala 2 using the kind projector plugin and Scala 3.0 - 3.2 using the compiler option `-Ykind-projector`. There is also a migration path for users that want a one-time transition to syntax with `_` as a type parameter placeholder. With option `-Ykind-projector:underscores` Scala 3 will regard `_` as a type parameter placeholder, leaving `?` as the only syntax for wildcards. From 20d0d59b8a59214ea33fb304f262b492803cc4e7 Mon Sep 17 00:00:00 2001 From: Bersier Date: Tue, 2 Jan 2024 05:46:47 -0500 Subject: [PATCH 141/144] Update context-functions.md Mainly fixed indentation --- .../reference/contextual/context-functions.md | 124 ++++++++++-------- 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/docs/_docs/reference/contextual/context-functions.md b/docs/_docs/reference/contextual/context-functions.md index 0ad3c8757782..0d174583f230 100644 --- a/docs/_docs/reference/contextual/context-functions.md +++ b/docs/_docs/reference/contextual/context-functions.md @@ -8,27 +8,29 @@ _Context functions_ are functions with (only) context parameters. Their types are _context function types_. Here is an example of a context function type: ```scala +import scala.concurrent.ExecutionContext + type Executable[T] = ExecutionContext ?=> T ``` Context functions are written using `?=>` as the "arrow" sign. They are applied to synthesized arguments, in the same way methods with context parameters are applied. For instance: ```scala - given ec: ExecutionContext = ... +given ec: ExecutionContext = ... - def f(x: Int): ExecutionContext ?=> Int = ... +def f(x: Int): ExecutionContext ?=> Int = ... - // could be written as follows with the type alias from above - // def f(x: Int): Executable[Int] = ... +// could be written as follows with the type alias from above +// def f(x: Int): Executable[Int] = ... - f(2)(using ec) // explicit argument - f(2) // argument is inferred +f(2)(using ec) // explicit argument +f(2) // argument is inferred ``` Conversely, if the expected type of an expression `E` is a context function type `(T_1, ..., T_n) ?=> U` and `E` is not already an context function literal, `E` is converted to a context function literal by rewriting it to ```scala - (x_1: T1, ..., x_n: Tn) ?=> E +(x_1: T1, ..., x_n: Tn) ?=> E ``` where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed before the expression `E` is typechecked, which means that `x_1`, ..., `x_n` @@ -38,14 +40,14 @@ Like their types, context function literals are written using `?=>` as the arrow For example, continuing with the previous definitions, ```scala - def g(arg: Executable[Int]) = ... +def g(arg: Executable[Int]) = ... - g(22) // is expanded to g((ev: ExecutionContext) ?=> 22) +g(22) // is expanded to g((ev: ExecutionContext) ?=> 22) - g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev)) +g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev)) - g((ctx: ExecutionContext) ?=> f(3)) // is expanded to g((ctx: ExecutionContext) ?=> f(3)(using ctx)) - g((ctx: ExecutionContext) ?=> f(3)(using ctx)) // is left as it is +g((ctx: ExecutionContext) ?=> f(3)) // is expanded to g((ctx: ExecutionContext) ?=> f(3)(using ctx)) +g((ctx: ExecutionContext) ?=> f(3)(using ctx)) // is left as it is ``` ## Example: Builder Pattern @@ -54,63 +56,65 @@ Context function types have considerable expressive power. For instance, here is how they can support the "builder pattern", where the aim is to construct tables like this: ```scala - table { - row { - cell("top left") - cell("top right") - } - row { - cell("bottom left") - cell("bottom right") - } +table { + row { + cell("top left") + cell("top right") + } + row { + cell("bottom left") + cell("bottom right") } +} ``` The idea is to define classes for `Table` and `Row` that allow the addition of elements via `add`: ```scala - class Table: - val rows = new ArrayBuffer[Row] - def add(r: Row): Unit = rows += r - override def toString = rows.mkString("Table(", ", ", ")") +import scala.collection.mutable.ArrayBuffer + +class Table: + val rows = new ArrayBuffer[Row] + def add(r: Row): Unit = rows += r + override def toString = rows.mkString("Table(", ", ", ")") - class Row: - val cells = new ArrayBuffer[Cell] - def add(c: Cell): Unit = cells += c - override def toString = cells.mkString("Row(", ", ", ")") +class Row: + val cells = new ArrayBuffer[Cell] + def add(c: Cell): Unit = cells += c + override def toString = cells.mkString("Row(", ", ", ")") - case class Cell(elem: String) +case class Cell(elem: String) ``` Then, the `table`, `row` and `cell` constructor methods can be defined with context function types as parameters to avoid the plumbing boilerplate that would otherwise be necessary. ```scala - def table(init: Table ?=> Unit) = - given t: Table = Table() - init - t - - def row(init: Row ?=> Unit)(using t: Table) = - given r: Row = Row() - init - t.add(r) - - def cell(str: String)(using r: Row) = - r.add(new Cell(str)) +def table(init: Table ?=> Unit) = + given t: Table = Table() + init + t + +def row(init: Row ?=> Unit)(using t: Table) = + given r: Row = Row() + init + t.add(r) + +def cell(str: String)(using r: Row) = + r.add(new Cell(str)) ``` With that setup, the table construction code above compiles and expands to: ```scala - table { ($t: Table) ?=> - - row { ($r: Row) ?=> - cell("top left")(using $r) - cell("top right")(using $r) - }(using $t) - - row { ($r: Row) ?=> - cell("bottom left")(using $r) - cell("bottom right")(using $r) - }(using $t) - } +table { ($t: Table) ?=> + + row { ($r: Row) ?=> + cell("top left")(using $r) + cell("top right")(using $r) + }(using $t) + + row { ($r: Row) ?=> + cell("bottom left")(using $r) + cell("bottom right")(using $r) + }(using $t) +} ``` ## Example: Postconditions @@ -131,12 +135,18 @@ import PostConditions.{ensuring, result} val s = List(1, 2, 3).sum.ensuring(result == 6) ``` -**Explanations**: We use a context function type `WrappedResult[T] ?=> Boolean` +### Explanation + +We use a context function type `WrappedResult[T] ?=> Boolean` as the type of the condition of `ensuring`. An argument to `ensuring` such as `(result == 6)` will therefore have a given of type `WrappedResult[T]` in -scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure +scope to pass along to the `result` method. + +`WrappedResult` is a fresh type, to make sure that we do not get unwanted givens in scope (this is good practice in all cases -where context parameters are involved). Since `WrappedResult` is an opaque type alias, its +where context parameters are involved). + +Since `WrappedResult` is an opaque type alias, its values need not be boxed, and since `ensuring` is added as an extension method, its argument does not need boxing either. Hence, the implementation of `ensuring` is close in efficiency to the best possible code one could write by hand: From ec4299464bdbabad83892b442cc9533d2a4cf6a7 Mon Sep 17 00:00:00 2001 From: Stephane Bersier Date: Mon, 29 Jan 2024 05:45:20 -0500 Subject: [PATCH 142/144] Update derivation.md Attempted small grammar improvements --- docs/_docs/reference/contextual/derivation.md | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/_docs/reference/contextual/derivation.md b/docs/_docs/reference/contextual/derivation.md index 66d0cf3fdf38..5157bb57699c 100644 --- a/docs/_docs/reference/contextual/derivation.md +++ b/docs/_docs/reference/contextual/derivation.md @@ -104,7 +104,7 @@ given TC[DerivingType] = TC.derived // simplified form of: given TC[ [A_1, ..., A_K] =>> DerivingType[A_1, ..., A_K] ] = TC.derived ``` -If `DerivingType` takes less arguments than `F` (`N < K`), we use only the rightmost parameters from the type lambda: +If `DerivingType` takes fewer arguments than `F` (`N < K`), we use only the rightmost parameters from the type lambda: ```scala given TC[ [A_1, ..., A_K] =>> DerivingType[A_(K-N+1), ..., A_K] ] = TC.derived @@ -112,7 +112,7 @@ given TC[ [A_1, ..., A_K] =>> DerivingType[A_(K-N+1), ..., A_K] ] = TC.derived given TC[ [A_1, ..., A_K] =>> DerivingType ] = TC.derived ``` -If `F` takes less arguments than `DerivingType` (`K < N`), we fill in the remaining leftmost slots with type parameters of the given: +If `F` takes fewer arguments than `DerivingType` (`K < N`), we fill in the remaining leftmost slots with type parameters of the given: ```scala given [T_1, ... T_(N-K)]: TC[[A_1, ..., A_K] =>> DerivingType[T_1, ... T_(N-K), A_1, ..., A_K]] = TC.derived ``` @@ -158,7 +158,7 @@ of the `Mirror` type class available. ## `Mirror` `scala.deriving.Mirror` type class instances provide information at the type level about the components and labelling of the type. -They also provide minimal term level infrastructure to allow higher level libraries to provide comprehensive +They also provide minimal term-level infrastructure to allow higher-level libraries to provide comprehensive derivation support. Instances of the `Mirror` type class are generated automatically by the compiler @@ -269,14 +269,14 @@ No given instance of type deriving.Mirror.Of[A] was found for parameter x of met Note the following properties of `Mirror` types, + Properties are encoded using types rather than terms. This means that they have no runtime footprint unless used and - also that they are a compile time feature for use with Scala 3's metaprogramming facilities. + also that they are a compile-time feature for use with Scala 3's metaprogramming facilities. + There is no restriction against the mirrored type being a local or inner class. + The kinds of `MirroredType` and `MirroredElemTypes` match the kind of the data type the mirror is an instance for. This allows `Mirror`s to support ADTs of all kinds. + There is no distinct representation type for sums or products (ie. there is no `HList` or `Coproduct` type as in Scala 2 versions of Shapeless). Instead the collection of child types of a data type is represented by an ordinary, possibly parameterized, tuple type. Scala 3's metaprogramming facilities can be used to work with these tuple types - as-is, and higher level libraries can be built on top of them. + as-is, and higher-level libraries can be built on top of them. + For both product and sum types, the elements of `MirroredElemTypes` are arranged in definition order (i.e. `Branch[T]` precedes `Leaf[T]` in `MirroredElemTypes` for `Tree` because `Branch` is defined before `Leaf` in the source file). This means that `Mirror.Sum` differs in this respect from Shapeless's generic representation for ADTs in Scala 2, @@ -303,16 +303,16 @@ has a context `Mirror` parameter, or not at all (e.g. they might use some comple instance using Scala 3 macros or runtime reflection). We expect that (direct or indirect) `Mirror` based implementations will be the most common and that is what this document emphasises. -Type class authors will most likely use higher level derivation or generic programming libraries to implement -`derived` methods. An example of how a `derived` method might be implemented using _only_ the low level facilities +Type class authors will most likely use higher-level derivation or generic programming libraries to implement +`derived` methods. An example of how a `derived` method might be implemented using _only_ the low-level facilities described above and Scala 3's general metaprogramming features is provided below. It is not anticipated that type class authors would normally implement a `derived` method in this way, however this walkthrough can be taken as a guide for -authors of the higher level derivation libraries that we expect typical type class authors will use (for a fully +authors of the higher-level derivation libraries that we expect typical type class authors will use (for a fully worked out example of such a library, see [Shapeless 3](https://github.com/milessabin/shapeless/tree/shapeless-3)). -## How to write a type class `derived` method using low level mechanisms +## How to write a type class `derived` method using low-level mechanisms -The low-level method we will use to implement a type class `derived` method in this example exploits three new type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. +The low-level technique we will use to implement a type class `derived` method in this example exploits three new type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. Given this definition of the `Eq` type class, ```scala @@ -335,13 +335,13 @@ inline def derived[T](using m: Mirror.Of[T]): Eq[T] = ``` Note that `derived` is defined as an `inline def`. -This means that the method will be inlined at all call sites (for instance the compiler generated instance definitions in the companion objects of ADTs which have a `deriving Eq` clause). +This means that the method will be inlined at all call sites (for instance the compiler-generated instance definitions in the companion objects of ADTs which have a `deriving Eq` clause). > Inlining of complex code is potentially expensive if overused (meaning slower compile times) so we should be careful to limit how many times `derived` is called for the same type. -> For example, when computing an instance for a sum type, it may be necessary to call `derived` recursively to compute an instance for a one of its child cases. +> For example, when computing an instance for a sum type, it may be necessary to call `derived` recursively to compute an instance for each one of its child cases. > That child case may in turn be a product type, that declares a field referring back to the parent sum type. > To compute the instance for this field, we should not call `derived` recursively, but instead summon from the context. -> Typically the found given instance will be the root given instance that initially called `derived`. +> Typically, the found given instance will be the root given instance that initially called `derived`. The body of `derived` (1) first materializes the `Eq` instances for all the child types of type the instance is being derived for. This is either all the branches of a sum type or all the fields of a product type. @@ -380,7 +380,7 @@ def eqSum[T](s: Mirror.SumOf[T], elems: => List[Eq[?]]): Eq[T] = (s.ordinal(y) == ordx) && check(x, y, elems(ordx)) // (4) ``` -In the product case, `eqProduct` we test the runtime values of the arguments to `eqv` for equality as products based on the `Eq` instances for the fields of the data type (5), +In the product case, `eqProduct`, we test the runtime values of the arguments to `eqv` for equality as products based on the `Eq` instances for the fields of the data type (5), ```scala import scala.deriving.Mirror @@ -486,7 +486,7 @@ Alternative approaches can be taken to the way that `derived` methods can be def inlined variants using Scala 3 macros, whilst being more involved for type class authors to write than the example above, can produce code for type classes like `Eq` which eliminate all the abstraction artefacts (eg. the `Lists` of child instances in the above) and generate code which is indistinguishable from what a programmer might write by hand. -As a third example, using a higher level library such as Shapeless the type class author could define an equivalent +As a third example, using a higher-level library such as Shapeless, the type class author could define an equivalent `derived` method as, ```scala @@ -508,7 +508,7 @@ inline def derived[A](using gen: K0.Generic[A]): Eq[A] = The framework described here enables all three of these approaches without mandating any of them. For a brief discussion on how to use macros to write a type class `derived` -method please read more at [How to write a type class `derived` method using macros](./derivation-macro.md). +method, please read more at [How to write a type class `derived` method using macros](./derivation-macro.md). ## Syntax @@ -539,22 +539,22 @@ This type class derivation framework is intentionally very small and low-level. infrastructure in compiler-generated `Mirror` instances, + type members encoding properties of the mirrored types. -+ a minimal value level mechanism for working generically with terms of the mirrored types. ++ a minimal value-level mechanism for working generically with terms of the mirrored types. The `Mirror` infrastructure can be seen as an extension of the existing `Product` infrastructure for case classes: -typically `Mirror` types will be implemented by the ADTs companion object, hence the type members and the `ordinal` or +typically, `Mirror` types will be implemented by the ADTs companion object, hence the type members and the `ordinal` or `fromProduct` methods will be members of that object. The primary motivation for this design decision, and the decision to encode properties via types rather than terms was to keep the bytecode and runtime footprint of the feature small enough to make it possible to provide `Mirror` instances _unconditionally_. -Whilst `Mirrors` encode properties precisely via type members, the value level `ordinal` and `fromProduct` are +Whilst `Mirrors` encode properties precisely via type members, the value-level `ordinal` and `fromProduct` are somewhat weakly typed (because they are defined in terms of `MirroredMonoType`) just like the members of `Product`. This means that code for generic type classes has to ensure that type exploration and value selection proceed in lockstep and it has to assert this conformance in some places using casts. If generic type classes are correctly written these casts will never fail. -As mentioned, however, the compiler-provided mechanism is intentionally very low level and it is anticipated that -higher level type class derivation and generic programming libraries will build on this and Scala 3's other +As mentioned, however, the compiler-provided mechanism is intentionally very low-level and it is anticipated that +higher-level type class derivation and generic programming libraries will build on this and Scala 3's other metaprogramming facilities to hide these low-level details from type class authors and general users. Type class derivation in the style of both Shapeless and Magnolia are possible (a prototype of Shapeless 3, which combines aspects of both Shapeless 2 and Magnolia has been developed alongside this language feature) as is a more aggressively From 1627f0586de8d95ab938e02a483435f2e9faf279 Mon Sep 17 00:00:00 2001 From: Stephane Bersier Date: Mon, 29 Jan 2024 06:45:44 -0500 Subject: [PATCH 143/144] Update derivation.md Added missing import --- docs/_docs/reference/contextual/derivation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/_docs/reference/contextual/derivation.md b/docs/_docs/reference/contextual/derivation.md index 5157bb57699c..ed0e005c1bd4 100644 --- a/docs/_docs/reference/contextual/derivation.md +++ b/docs/_docs/reference/contextual/derivation.md @@ -396,8 +396,9 @@ Both `eqSum` and `eqProduct` have a by-name parameter `elems`, because the argum Pulling this all together we have the following complete implementation, ```scala +import scala.collection.AbstractIterable +import scala.compiletime.{erasedValue, error, summonInline} import scala.deriving.* -import scala.compiletime.{error, erasedValue, summonInline} inline def summonInstances[T, Elems <: Tuple]: List[Eq[?]] = inline erasedValue[Elems] match From 97138fbb6eff1fa2206f48de8f20ec3a42805779 Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Wed, 14 Feb 2024 19:59:51 +0100 Subject: [PATCH 144/144] Fix the capture checking documentation The syntax on the official Scala page was outdated. Also, new docs nudge people to use the latest version of Scala, which allow trying capture checking out on a stable compiler version. --- docs/_docs/reference/experimental/cc.md | 178 +++++++++++------------- 1 file changed, 85 insertions(+), 93 deletions(-) diff --git a/docs/_docs/reference/experimental/cc.md b/docs/_docs/reference/experimental/cc.md index 2a7236453eab..5bdf91f628ec 100644 --- a/docs/_docs/reference/experimental/cc.md +++ b/docs/_docs/reference/experimental/cc.md @@ -8,7 +8,8 @@ Capture checking is a research project that modifies the Scala type system to tr ```scala import language.experimental.captureChecking ``` -At present, capture checking is still highly experimental and unstable. +At present, capture checking is still highly experimental and unstable, and it evolves quickly. +Before trying it out, make sure you have the latest version of Scala. To get an idea what capture checking can do, let's start with a small example: ```scala @@ -34,17 +35,17 @@ results in an uncaught `IOException`. Capture checking gives us the mechanism to prevent such errors _statically_. To prevent unsafe usages of `usingLogFile`, we can declare it like this: ```scala -def usingLogFile[T](op: ({*} FileOutputStream) => T): T = +def usingLogFile[T](op: FileOutputStream^ => T): T = // same body as before ``` The only thing that's changed is that the `FileOutputStream` parameter of `op` is now -tagged with `{*}`. We'll see that this turns the parameter into a _capability_ whose lifetime is tracked. +followed by `^`. We'll see that this turns the parameter into a _capability_ whose lifetime is tracked. If we now try to define the problematic value `later`, we get a static error: ``` | val later = usingLogFile { f => () => f.write(0) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + |The expression's type () => Unit is not allowed to capture the root capability `cap`. |This usually means that a capability persists longer than its allowed lifetime. ``` In this case, it was easy to see that the `logFile` capability escapes in the closure passed to `usingLogFile`. But capture checking also works for more complex cases. @@ -84,26 +85,27 @@ The capture checker extension introduces a new kind of types and it enforces som ## Capabilities and Capturing Types Capture checking is done in terms of _capturing types_ of the form -`{c₁, ..., cᔹ} T`. Here `T` is a type, and `{c₁, ..., cᔹ}` is a _capture set_ consisting of references to capabilities `c₁, ..., cᔹ`. +`T^{c₁, ..., cᔹ}`. Here `T` is a type, and `{c₁, ..., cᔹ}` is a _capture set_ consisting of references to capabilities `c₁, ..., cᔹ`. A _capability_ is syntactically a method- or class-parameter, a local variable, or the `this` of an enclosing class. The type of a capability must be a capturing type with a non-empty capture set. We also say that variables that are capabilities are _tracked_. In a sense, every -capability gets its authority from some other, more sweeping capability which it captures. The most sweeping capability, from which ultimately all others are derived is written `*`. We call it the _universal capability_. +capability gets its authority from some other, more sweeping capability which it captures. The most sweeping capability, from which ultimately all others are derived is written `cap`. We call it the _universal capability_. +If `T` is a type, then `T^` is a shorthand for `T^{cap}`, meaning `T` can capture arbitrary capabilities. Here is an example: ```scala class FileSystem -class Logger(fs: {*} FileSystem): +class Logger(fs: FileSystem^): def log(s: String): Unit = ... // Write to a log file, using `fs` -def test(fs: {*} FileSystem) = - val l: {fs} Logger = Logger(fs) +def test(fs: FileSystem^) = + val l: Logger^{fs} = Logger(fs) l.log("hello world!") - val xs: {l} LazyList[Int] = + val xs: LazyList[Int]^{l} = LazyList.from(1) .map { i => l.log(s"computing elem # $i") @@ -113,12 +115,12 @@ def test(fs: {*} FileSystem) = ``` Here, the `test` method takes a `FileSystem` as a parameter. `fs` is a capability since its type has a non-empty capture set. The capability is passed to the `Logger` constructor and retained as a field in class `Logger`. Hence, the local variable `l` has type -`{fs} Logger`: it is a `Logger` which retains the `fs` capability. +`Logger^{fs}`: it is a `Logger` which retains the `fs` capability. The second variable defined in `test` is `xs`, a lazy list that is obtained from `LazyList.from(1)` by logging and mapping consecutive numbers. Since the list is lazy, it needs to retain the reference to the logger `l` for its computations. Hence, the -type of the list is `{l} LazyList[Int]`. On the other hand, since `xs` only logs but does +type of the list is `LazyList[Int]^{l}`. On the other hand, since `xs` only logs but does not do other file operations, it retains the `fs` capability only indirectly. That's why `fs` does not show up in the capture set of `xs`. @@ -129,24 +131,14 @@ any capturing type that adds a capture set to `T`. The usual function type `A => B` now stands for a function that can capture arbitrary capabilities. We call such functions _impure_. By contrast, the new single arrow function type `A -> B` stands for a function that cannot capture any capabilities, or otherwise said, is _pure_. -One can add a capture set in front of an otherwise pure function. -For instance, `{c, d} A -> B` would be a function that can capture capabilities `c` and `d`, but no others. +One can add a capture set after the arrow of an otherwise pure function. +For instance, `A ->{c, d} B` would be a function that can capture capabilities `c` and `d`, but no others. +This type is a shorthand for `(A -> B)^{c, d}`, i.e. the function type `A -> B` with possible captures `{c, d}`. -The impure function type `A => B` is treated as an alias for `{*} A -> B`. That is, impure functions are functions that can capture anything. +The impure function type `A => B` is treated as an alias for `A ->{cap} B`. That is, impure functions are functions that can capture anything. -Function types and captures both associate to the right, so -```scala -{c} A -> {d} B -> C -``` -is the same as -```scala -{c} (A -> {d} (B -> C)) -``` -Contrast with -```scala -({c} A) -> ({d} B) -> C -``` -which is a curried pure function over argument types that can capture `c` and `d`, respectively. +A capture annotation `^` binds more strongly than a function arrow. So +`A -> B^{c}` is read as `A` -> (B^{c})`. Analogous conventions apply to context function types. `A ?=> B` is an impure context function, with `A ?-> B` as its pure complement. @@ -173,13 +165,10 @@ def f(x: -> Int): Int the actual argument to `f` could not refer to any capabilities, so the call above would be rejected. One can also allow specific capabilities like this: ```scala -def f(x: {c}-> Int): Int +def f(x: ->{c} Int): Int ``` Here, the actual argument to `f` is allowed to use the `c` capability but no others. -**Note:** It is strongly recommended to write the capability set and the arrow `->` without intervening spaces, -as otherwise the notation would look confusingly like a function type. - ## Subtyping and Subcapturing Capturing influences subtyping. As usual we write `T₁ <: T₂` to express that the type @@ -201,35 +190,35 @@ A subcapturing relation `C₁ <: C₂` holds if `C₂` _accounts for_ every elem **Example 1.** Given ```scala -fs: {*} FileSystem -ct: {*} CanThrow[Exception] -l : {fs} Logger +fs: FileSystem^ +ct: CanThrow[Exception]^ +l : Logger^{fs} ``` we have ``` -{l} <: {fs} <: {*} -{fs} <: {fs, ct} <: {*} -{ct} <: {fs, ct} <: {*} +{l} <: {fs} <: {cap} +{fs} <: {fs, ct} <: {cap} +{ct} <: {fs, ct} <: {cap} ``` -The set consisting of the root capability `{*}` covers every other capture set. This is -a consequence of the fact that, ultimately, every capability is created from `*`. +The set consisting of the root capability `{cap}` covers every other capture set. This is +a consequence of the fact that, ultimately, every capability is created from `cap`. -**Example 2.** Consider again the FileSystem/Logger example from before. `LazyList[Int]` is a proper subtype of `{l} LazyList[Int]`. So if the `test` method in that example +**Example 2.** Consider again the FileSystem/Logger example from before. `LazyList[Int]` is a proper subtype of `LazyList[Int]^{l}`. So if the `test` method in that example was declared with a result type `LazyList[Int]`, we'd get a type error. Here is the error message: ``` -11 |def test(using fs: {*} FileSystem): LazyList[Int] = { - | ^ - | Found: {fs} LazyList[Int] - | Required: LazyList[Int] +11 |def test(using fs: FileSystem^): LazyList[Int] = { + | ^ + | Found: LazyList[Int]^{fs} + | Required: LazyList[Int] ``` -Why does it say `{fs} LazyList[Int]` and not `{l} LazyList[Int]`, which is, after all, the type of the returned value `xs`? The reason is that `l` is a local variable in the body of `test`, so it cannot be referred to in a type outside that body. What happens instead is that the type is _widened_ to the smallest supertype that does not mention `l`. Since `l` has capture set `fs`, we have that `{fs}` covers `{l}`, and `{fs}` is acceptable in a result type of `test`, so `{fs}` is the result of that widening. +Why does it say `LazyList[Int]^{fs}` and not `LazyList[Int]^{l}`, which is, after all, the type of the returned value `xs`? The reason is that `l` is a local variable in the body of `test`, so it cannot be referred to in a type outside that body. What happens instead is that the type is _widened_ to the smallest supertype that does not mention `l`. Since `l` has capture set `fs`, we have that `{fs}` covers `{l}`, and `{fs}` is acceptable in a result type of `test`, so `{fs}` is the result of that widening. This widening is called _avoidance_; it is not specific to capture checking but applies to all variable references in Scala types. ## Capability Classes Classes like `CanThrow` or `FileSystem` have the property that their values are always intended to be capabilities. We can make this intention explicit and save boilerplate by declaring these classes with a `@capability` annotation. -The capture set of a capability class type is always `{*}`. This means we could equivalently express the `FileSystem` and `Logger` classes as follows: +The capture set of a capability class type is always `{cap}`. This means we could equivalently express the `FileSystem` and `Logger` classes as follows: ```scala import annotation.capability @@ -239,14 +228,14 @@ class Logger(using FileSystem): def log(s: String): Unit = ??? def test(using fs: FileSystem) = - val l: {fs} Logger = Logger() + val l: Logger^{fs} = Logger() ... ``` -In this version, `FileSystem` is a capability class, which means that the `{*}` capture set is implied on the parameters of `Logger` and `test`. Writing the capture set explicitly produces a warning: +In this version, `FileSystem` is a capability class, which means that the `{cap}` capture set is implied on the parameters of `Logger` and `test`. Writing the capture set explicitly produces a warning: ```scala -class Logger(using {*} FileSystem): +class Logger(using FileSystem^{cap}): ^^^^^^^^^^^^^^ - redundant capture: FileSystem already accounts for * + redundant capture: FileSystem already accounts for cap ``` Another, unrelated change in the version of the last example here is that the `FileSystem` capability is now passed as an implicit parameter. It is quite natural to model capabilities with implicit parameters since it greatly reduces the wiring overhead once multiple capabilities are in play. @@ -254,11 +243,11 @@ Another, unrelated change in the version of the last example here is that the `F If a closure refers to capabilities in its body, it captures these capabilities in its type. For instance, consider: ```scala -def test(fs: FileSystem): {fs} String -> Unit = +def test(fs: FileSystem): String ->{fs} Unit = (x: String) => Logger(fs).log(x) ``` Here, the body of `test` is a lambda that refers to the capability `fs`, which means that `fs` is retained in the lambda. -Consequently, the type of the lambda is `{fs} String -> Unit`. +Consequently, the type of the lambda is `String ->{fs} Unit`. **Note:** Function values are always written with `=>` (or `?=>` for context functions). There is no syntactic distinction for pure _vs_ impure function values. The distinction is only made in their types. @@ -271,7 +260,7 @@ def test(fs: FileSystem) = def g() = (x: String) => Logger(fs).log(x) f ``` -the result of `test` has type `{fs} String -> Unit` even though function `f` itself does not refer to `fs`. +the result of `test` has type `String ->{fs} Unit` even though function `f` itself does not refer to `fs`. ## Capture Checking of Classes @@ -280,11 +269,11 @@ The principles for capture checking closures also apply to classes. For instance class Logger(using fs: FileSystem): def log(s: String): Unit = ... summon[FileSystem] ... -def test(xfs: FileSystem): {xfs} Logger = +def test(xfs: FileSystem): Logger^{xfs} = Logger(xfs) ``` Here, class `Logger` retains the capability `fs` as a (private) field. Hence, the result -of `test` is of type `{xfs} Logger` +of `test` is of type `Logger^{xfs}` Sometimes, a tracked capability is meant to be used only in the constructor of a class, but is not intended to be retained as a field. This fact can be communicated to the capture @@ -317,7 +306,7 @@ the capture set of that call is `{a, b, c}`. The capture set of the type of `this` of a class is inferred by the capture checker, unless the type is explicitly declared with a self type annotation like this one: ```scala class C: - self: {a, b} D => ... + self: D^{a, b} => ... ``` The inference observes the following constraints: @@ -351,13 +340,13 @@ class Pair[+A, +B](x: A, y: B): ``` What happens if `Pair` is instantiated like this (assuming `ct` and `fs` are two capabilities in scope)? ```scala -def x: {ct} Int -> String -def y: {fs} Logger +def x: Int ->{ct} String +def y: Logger^{fs} def p = Pair(x, y) ``` The last line will be typed as follows: ```scala -def p: Pair[{ct} Int -> String, {fs} Logger] = Pair(x, y) +def p: Pair[Int ->{ct} String, Logger^{fs}] = Pair(x, y) ``` This might seem surprising. The `Pair(x, y)` value does capture capabilities `ct` and `fs`. Why don't they show up in its type at the outside? @@ -365,53 +354,53 @@ The answer is capture tunnelling. Once a type variable is instantiated to a capt capture is not propagated beyond this point. On the other hand, if the type variable is instantiated again on access, the capture information "pops out" again. For instance, even though `p` is technically pure because its capture set is empty, writing `p.fst` would record a reference to the captured capability `ct`. So if this access was put in a closure, the capability would again form part of the outer capture set. E.g. ```scala -() => p.fst : {ct} () -> {ct} Int -> String +() => p.fst : () -> Int ->{ct} String ``` In other words, references to capabilities "tunnel through" in generic instantiations from creation to access; they do not affect the capture set of the enclosing generic data constructor applications. This principle plays an important part in making capture checking concise and practical. ## Escape Checking -The universal capability `*` should be conceptually available only as a parameter to the main program. Indeed, if it was available everywhere, capability checking would be undermined since one could mint new capabilities -at will. In line with this reasoning, some capture sets are restricted so that +Some capture sets are restricted so that they are not allowed to contain the universal capability. Specifically, if a capturing type is an instance of a type variable, that capturing type -is not allowed to carry the universal capability `{*}`. There's a connection to tunnelling here. +is not allowed to carry the universal capability `cap`. There's a connection to tunnelling here. The capture set of a type has to be present in the environment when a type is instantiated from -a type variable. But `*` is not itself available as a global entity in the environment. Hence, +a type variable. But `cap` is not itself available as a global entity in the environment. Hence, an error should result. We can now reconstruct how this principle produced the error in the introductory example, where `usingLogFile` was declared like this: ```scala -def usingLogFile[T](op: ({*} FileOutputStream) => T): T = ... +def usingLogFile[T](op: FileOutputStream^ => T): T = ... ``` The error message was: ``` | val later = usingLogFile { f => () => f.write(0) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + |The expression's type () => Unit is not allowed to capture the root capability `cap`. |This usually means that a capability persists longer than its allowed lifetime. ``` This error message was produced by the following logic: - - The `f` parameter has type `{*} FileOutputStream`, which makes it a capability. - - Therefore, the type of the expression `() => f.write(0)` is `{f} () -> Unit`. + - The `f` parameter has type `FileOutputStream^`, which makes it a capability. + - Therefore, the type of the expression `() => f.write(0)` is `() ->{f} Unit`. - This makes the type of the whole closure passed to `usingLogFile` the dependent function type - `(f: {*} FileOutputStream) -> {f} () -> Unit`. - - The expected type of the closure is a simple, parametric, impure function type `({*} FileOutputStream) => T`, + `(f: FileOutputStream^) -> () ->{f} Unit`. + - The expected type of the closure is a simple, parametric, impure function type `FileOutputStream^ => T`, for some instantiation of the type variable `T`. - The smallest supertype of the closure's dependent function type that is a parametric function type is - `({*} FileOutputStream) => {*} () -> Unit` - - Hence, the type variable `T` is instantiated to `* () -> Unit`, which causes the error. + `FileOutputStream^ => () ->{cap} Unit` + - Hence, the type variable `T` is instantiated to `() ->{cap} Unit`, or abbreviated `() => Unit`, + which causes the error. An analogous restriction applies to the type of a mutable variable. Another way one could try to undermine capture checking would be to assign a closure with a local capability to a global variable. Maybe like this: ```scala -var loophole: {*} () -> Unit = () => () +var loophole: () => Unit = () => () usingLogFile { f => loophole = () => f.write(0) } @@ -427,11 +416,11 @@ val sneaky = usingLogFile { f => Cell(() => f.write(0)) } sneaky.x() ``` At the point where the `Cell` is created, the capture set of the argument is `f`, which -is OK. But at the point of use, it is `*` (because `f` is no longer in scope), which causes again an error: +is OK. But at the point of use, it is `cap` (because `f` is no longer in scope), which causes again an error: ``` | sneaky.x() | ^^^^^^^^ - |The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + |The expression's type () => Unit is not allowed to capture the root capability `cap`. |This usually means that a capability persists longer than its allowed lifetime. ``` @@ -507,7 +496,7 @@ Under the language import `language.experimental.captureChecking`, the code is i ``` 14 | try () => xs.map(f).sum | ^ - |The expression's type {*} () -> Double is not allowed to capture the root capability `*`. + |The expression's type () => Double is not allowed to capture the root capability `cap`. |This usually means that a capability persists longer than its allowed lifetime. 15 | catch case ex: LimitExceeded => () => -1 ``` @@ -527,7 +516,7 @@ Here is the base trait `LzyList` for our version of lazy lists: trait LzyList[+A]: def isEmpty: Boolean def head: A - def tail: {this} LzyList[A] + def tail: LzyList[A]^{this} ``` Note that `tail` carries a capture annotation. It says that the tail of a lazy list can potentially capture the same references as the lazy list as a whole. @@ -543,16 +532,16 @@ Here is a formulation of the class for lazy cons nodes: ```scala import scala.compiletime.uninitialized -final class LzyCons[+A](hd: A, tl: () => {*} LzyList[A]) extends LzyList[A]: +final class LzyCons[+A](hd: A, tl: () => LzyList[A]^) extends LzyList[A]: private var forced = false - private var cache: {this} LzyList[A] = uninitialized + private var cache: LzyList[A]^{this} = uninitialized private def force = if !forced then { cache = tl(); forced = true } cache def isEmpty = false def head = hd - def tail: {this} LzyList[A] = force + def tail: LzyList[A]^{this} = force end LzyCons ``` The `LzyCons` class takes two parameters: A head `hd` and a tail `tl`, which is a function @@ -564,7 +553,7 @@ Here is an extension method to define an infix cons operator `#:` for lazy lists to `::` but instead of a strict list it produces a lazy list without evaluating its right operand. ```scala extension [A](x: A) - def #:(xs1: => {*} LzyList[A]): {xs1} LzyList[A] = + def #:(xs1: => LzyList[A]^): LzyList[A]^{xs1} = LzyCons(x, () => xs1) ``` Note that `#:` takes an impure call-by-name parameter `xs1` as its right argument. The result @@ -575,7 +564,7 @@ of given length with a generator function `gen`. The generator function is allow to have side effects. ```scala def tabulate[A](n: Int)(gen: Int => A) = - def recur(i: Int): {gen} LzyList[A] = + def recur(i: Int): LzyList[A]^{gen} = if i == n then LzyNil else gen(i) #: recur(i + 1) recur(0) @@ -584,32 +573,31 @@ Here is a use of `tabulate`: ```scala class LimitExceeded extends Exception def squares(n: Int)(using ct: CanThrow[LimitExceeded]) = - tabulate(10) { i => + tabulate(10): i => if i > 9 then throw LimitExceeded() i * i - } ``` -The inferred result type of `squares` is `{ct} LzyList[Int]`, i.e it is a lazy list of +The inferred result type of `squares` is `LzyList[Int]^{ct}`, i.e it is a lazy list of `Int`s that can throw the `LimitExceeded` exception when it is elaborated by calling `tail` one or more times. Here are some further extension methods for mapping, filtering, and concatenating lazy lists: ```scala -extension [A](xs: {*} LzyList[A]) - def map[B](f: A => B): {xs, f} LzyList[B] = +extension [A](xs: LzyList[A]^) + def map[B](f: A => B): LzyList[B]^{xs, f} = if xs.isEmpty then LzyNil else f(xs.head) #: xs.tail.map(f) - def filter(p: A => Boolean): {xs, p} LzyList[A] = + def filter(p: A => Boolean): LzyList[A]^{xs, p} = if xs.isEmpty then LzyNil else if p(xs.head) then xs.head #: xs.tail.filter(p) else xs.tail.filter(p) - def concat(ys: {*} LzyList[A]): {xs, ys} LzyList[A] = + def concat(ys: LzyList[A]^): LzyList[A]^{xs, ys} = if xs.isEmpty then ys else xs.head #: xs.tail.concat(ys) - def drop(n: Int): {xs} LzyList[A] = + def drop(n: Int): LzyList[A]^{xs} = if n == 0 then xs else xs.tail.drop(n - 1) ``` Their capture annotations are all as one would expect: @@ -621,11 +609,11 @@ Their capture annotations are all as one would expect: - Concatenating two lazy lists produces a lazy list that captures both arguments. - Dropping elements from a lazy list gives a safe approximation where the original list is captured in the result. In fact, it's only some suffix of the list that is retained at run time, but our modelling identifies lazy lists and their suffixes, so this additional knowledge would not be useful. -Of course the function passed to `map` or `filter` could also be pure. After all, `A -> B` is a subtype of `{*} A -> B` which is the same as `A => B`. In that case, the pure function +Of course the function passed to `map` or `filter` could also be pure. After all, `A -> B` is a subtype of `(A -> B)^{cap}` which is the same as `A => B`. In that case, the pure function argument will _not_ show up in the result type of `map` or `filter`. For instance: ```scala val xs = squares(10) -val ys: {xs} LzyList[Int] = xs.map(_ + 1) +val ys: LzyList[Int]^{xs} = xs.map(_ + 1) ``` The type of the mapped list `ys` has only `xs` in its capture set. The actual function argument does not show up since it is pure. Likewise, if the lazy list @@ -718,6 +706,8 @@ Generally, the string following the capture set consists of alternating numbers - `F` : a variable resulting from _filtering_ the elements of the variable indicated by the string to the right, - `I` : a variable resulting from an _intersection_ of two capture sets, - `D` : a variable resulting from the set _difference_ of two capture sets. + - `R` : a regular variable that _refines_ a class parameter, so that the capture + set of a constructor argument is known in the class instance type. At the end of a compilation run, `-Ycc-debug` will print all variable dependencies of variables referred to in previous output. Here is an example: ``` @@ -736,3 +726,5 @@ This section lists all variables that appeared in previous diagnostics and their - variable `31` has a constant fixed superset `{xs, f}` - variable `32` has no dependencies. + +