ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
@ 2024-01-23 15:26 byroot (Jean Boussier) via ruby-core
  2024-01-23 18:32 ` [ruby-core:116386] " matheusrich (Matheus Richard) via ruby-core
                   ` (51 more replies)
  0 siblings, 52 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-23 15:26 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been reported by byroot (Jean Boussier).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116386] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
@ 2024-01-23 18:32 ` matheusrich (Matheus Richard) via ruby-core
  2024-01-24  4:38 ` [ruby-core:116390] " mame (Yusuke Endoh) via ruby-core
                   ` (50 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: matheusrich (Matheus Richard) via ruby-core @ 2024-01-23 18:32 UTC (permalink / raw
  To: ruby-core; +Cc: matheusrich (Matheus Richard)

Issue #20205 has been updated by matheusrich (Matheus Richard).


> Given that rubocop is quite popular in the community and it has enforced the usage of # frozen_string_literal: true for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

That's true, but because standardrb doesn't enforce it (and many folks have been defaulting to that), I've seen several codebases not consistently using the pragma.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106404

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116390] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
  2024-01-23 18:32 ` [ruby-core:116386] " matheusrich (Matheus Richard) via ruby-core
@ 2024-01-24  4:38 ` mame (Yusuke Endoh) via ruby-core
  2024-01-24  9:34 ` [ruby-core:116396] " zverok (Victor Shepelev) via ruby-core
                   ` (49 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2024-01-24  4:38 UTC (permalink / raw
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #20205 has been updated by mame (Yusuke Endoh).


I think we should evaluate the value of `# frozen_string_literal: true` before making it the default.

The purpose of `# frozen_string_literal: true` is to make Ruby code fast and memory-saving. When it was introduced, no quantitative evaluation was available except for micro-benchmarks, because most code did not support frozen_string_literal.

Now that there are many gems supporting `# frozen_string_literal: true`. So we can evaluate it. For example, how much would the performance degrade if we removed `# frozen_string_literal: true` from all code used in yjit-bench?

If the degradation is large enough, then making `# frozen_string_literal: true` the default may be worth serious consideration. If the degradation is negligible, it would be reasonable for Rubocop to stop enforcing `# frozen_string_literal: true` and remove the magic comments from existing gems as well.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106413

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116396] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
  2024-01-23 18:32 ` [ruby-core:116386] " matheusrich (Matheus Richard) via ruby-core
  2024-01-24  4:38 ` [ruby-core:116390] " mame (Yusuke Endoh) via ruby-core
@ 2024-01-24  9:34 ` zverok (Victor Shepelev) via ruby-core
  2024-01-24  9:44 ` [ruby-core:116397] " byroot (Jean Boussier) via ruby-core
                   ` (48 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: zverok (Victor Shepelev) via ruby-core @ 2024-01-24  9:34 UTC (permalink / raw
  To: ruby-core; +Cc: zverok (Victor Shepelev)

Issue #20205 has been updated by zverok (Victor Shepelev).


> The purpose of `# frozen_string_literal: true` is to make Ruby code fast and memory-saving. When it was introduced, no quantitative evaluation was available except for micro-benchmarks, because most code did not support frozen_string_literal.

I believe that whatever the initial intention, the "frozen string literals" concept being adopted by many codebases is also a cultural thing, not only performance-related. 

The "string literals are frozen by default" changes the way we program, if even slightly. That’s actually a long-standing topic to have more impactful freezing (of the constants, for example), but frozen string literals _at the very least_ prevent trivial errors like

```ruby
HEADER = "<html><body>"

def generate
  output = HEADER # no `.dup`
  output << "<p>test</p>" # actually changed HEADER
  # ...
end
```

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106418

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116397] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (2 preceding siblings ...)
  2024-01-24  9:34 ` [ruby-core:116396] " zverok (Victor Shepelev) via ruby-core
@ 2024-01-24  9:44 ` byroot (Jean Boussier) via ruby-core
  2024-01-24  9:46 ` [ruby-core:116398] " byroot (Jean Boussier) via ruby-core
                   ` (47 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24  9:44 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> I think we should evaluate the value of # frozen_string_literal: true before making it the default.

So it's not 100% reliable because I ran it locally rather than on a benchmarking server, as evidenced by some strange effect on benchmarks that don't normally deal with strings (e.g. `setivar_object`), but here are the results:

```
mutable: ruby 3.4.0dev (2024-01-24T08:24:16Z disable-frozen-str.. a39d5eae1e) [arm64-darwin23]
frozen: ruby 3.4.0dev (2024-01-24T08:24:16Z disable-frozen-str.. bb0cee8dab) [arm64-darwin23]

--------------  ------------  ----------  -----------  ----------  --------------  --------------
bench           mutable (ms)  stddev (%)  frozen (ms)  stddev (%)  frozen 1st itr  mutable/frozen
activerecord    32.9          4.0         32.7         5.3         1.06            1.01          
chunky-png      578.0         1.1         555.2        1.0         1.01            1.04          
erubi-rails     1124.2        2.4         1158.7       1.7         0.93            0.97          
hexapdf         1703.9        2.7         1670.2       2.2         1.04            1.02          
liquid-c        34.3          5.7         34.1         5.0         1.02            1.01          
liquid-compile  39.8          4.2         38.0         5.3         0.99            1.05          
liquid-render   97.2          3.4         97.7         3.5         1.01            0.99          
lobsters        637.8         5.2         614.9        4.2         1.11            1.04          
mail            82.7          3.7         82.1         3.7         1.00            1.01          
psych-load      1500.8        0.8         1487.6       0.5         0.99            1.01          
railsbench      1116.3        2.1         1099.8       1.6         1.02            1.01          
rubocop         112.0         3.8         111.2        3.7         1.03            1.01          
ruby-lsp        79.5          2.6         80.3         2.4         0.92            0.99          
sequel          36.4          4.4         36.5         3.3         0.95            1.00          
binarytrees     238.4         1.9         238.8        1.7         0.99            1.00          
blurhash        281.9         1.4         282.5        1.7         0.99            1.00          
erubi           166.1         2.0         171.5        2.5         0.97            0.97          
etanni          198.7         2.7         200.5        1.8         1.04            0.99          
fannkuchredux   2065.3        0.7         2078.5       0.5         0.99            0.99          
fluentd         1192.0        1.0         1202.7       0.7         1.03            0.99          
graphql         2323.6        0.7         2340.5       0.5         0.98            0.99          
graphql-native  385.3         1.5         384.2        2.1         1.00            1.00          
lee             739.3         1.8         750.1        1.2         0.98            0.99          
matmul          1494.2        0.8         1497.8       0.9         1.01            1.00          
nbody           71.1          2.3         71.3         5.1         1.00            1.00          
nqueens         169.8         1.6         167.6        2.0         1.00            1.01          
optcarrot       4202.7        0.6         4216.8       0.5         1.00            1.00          
rack            56.0          3.3         53.9         3.9         1.06            1.04          
ruby-json       1976.3        0.9         1992.4       0.3         1.00            0.99          
rubykon         6971.3        0.8         7053.4       0.4         1.00            0.99          
sudoku          1836.1        0.4         1836.3       0.3         1.00            1.00          
tinygql         441.4         1.0         447.4        1.2         0.96            0.99          
30k_ifelse      1459.0        8.6         1429.1       4.6         0.96            1.02          
30k_methods     3331.8        3.7         3264.0       1.1         1.03            1.02          
cfunc_itself    81.5          1.8         81.2         2.9         1.02            1.00          
fib             187.0         1.6         188.8        1.5         0.94            0.99          
getivar         65.1          2.2         65.7         2.4         1.01            0.99          
keyword_args    141.6         2.3         140.9        2.1         1.00            1.01          
respond_to      184.9         1.9         185.7        1.1         0.96            1.00          
setivar         39.2          2.9         39.2         2.4         1.03            1.00          
setivar_object  72.7          2.5         77.5         2.1         0.95            0.94          
setivar_young   72.5          1.9         77.5         0.9         0.93            0.94          
str_concat      69.7          2.5         69.9         1.9         1.02            1.00          
throw           14.9          5.0         14.8         4.5         1.04            1.01          
--------------  ------------  ----------  -----------  ----------  --------------  --------------
Legend:
- frozen 1st itr: ratio of mutable/frozen time for the first benchmarking iteration.
- mutable/frozen: ratio of mutable/frozen time. Higher is better for frozen. Above 1 represents a speedup.

```


I used the following patch to disable frozen string literals globally: https://github.com/ruby/ruby/compare/master...Shopify:ruby:disable-frozen-string-literal, and ran the suite with `ruby run_benchmarks.rb --chruby 'mutable::head-mutable-strings;frozen::head'`.

Most of these benchmarks don't really deal with string, but one that I think is the most close to reality and to Ruby bread and butter is `lobsters` and it seem to be quite positive.

I also didn't enable YJIT, I suspect the difference would be bigger if I did, as the extra GC pressure would be relatively bigger.

I'll see about using the yjit-perf benchmark server to run these more scientifically.

I also want to note that I have to explictly freeze `RUBY_DESCRIPTION` in my patch, otherwise it would cause a Ractor issue, which is another argument for frozen strings, as they ease the necessary work necessary to make code Ractor compatible.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106419

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116398] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (3 preceding siblings ...)
  2024-01-24  9:44 ` [ruby-core:116397] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24  9:46 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 11:11 ` [ruby-core:116404] " duerst via ruby-core
                   ` (46 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24  9:46 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


>  but because standardrb doesn't enforce it (and many folks have been defaulting to that), I've seen several codebases not consistently using the pragma.

Yes, I didn't extend on my motivations for bringing this now, but It's in big part influenced by `standardrb` and some other communities starting to discourage the use of frozen string literals. I suspect many people are starting to be fed up with that magic comment and we probably reached peak usage of it.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106420

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116404] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (4 preceding siblings ...)
  2024-01-24  9:46 ` [ruby-core:116398] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 11:11 ` duerst via ruby-core
  2024-01-24 11:12 ` [ruby-core:116405] " Eregon (Benoit Daloze) via ruby-core
                   ` (45 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: duerst via ruby-core @ 2024-01-24 11:11 UTC (permalink / raw
  To: ruby-core; +Cc: duerst

Issue #20205 has been updated by duerst (Martin Dürst).





`frozen_string_literal` can lead to more efficient code, but it's also part of a programming style (usually called functional programming). Functional programming often leads to cleaner code, but it may require some additional programming effort. There's also a fundamental conflict between object-oriented programming (objects are generally mutable) and functional programming, although Ruby is pretty good at integrating these concepts.



My main issue with this proposal is that I think it's probably the right thing for most big code bases, but it may not be the right thing for quick-and-dirty small scripts. And Ruby is used, and should continue to be usable, for both kinds of code.



Maybe what could help is a declaration on a higher level, e.g. per gem or so rather than per source file. (That's just an idea, with many open questions: Where would the setting go? How would the interpreter pick it up? How would people become aware of it? ...)



----------------------------------------

Feature #20205: Enable `frozen_string_literal` by default

https://bugs.ruby-lang.org/issues/20205#change-106425



* Author: byroot (Jean Boussier)

* Status: Open

* Priority: Normal

----------------------------------------

### Context



The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.



According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 



The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.



The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.



One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.



So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.



### Deprecation Warning Implementation



I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549



In short:



- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.

- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.

- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.



Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.



But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,

we can record which warnings were already issued to avoid spamming users with duplicated warnings.



As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,

we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.



But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.



### Timeline



The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.

I don't have a strong opinion on the pace.



- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).

- Release `R1`: make the deprecation warning show up regardless of verbosity level.

- Release `R2`: make string literals frozen by default.



### Impact



Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,

I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.



And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),

the few that didn't migrate will likely be made compatible quickly.



The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,

and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.



### Workflow for library maintainers



As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.



Alternatively they can of course make their code compatible with frozen string literals.



Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.



### Workflow for application owners



For application owners, the workflow is the same than for libraries.



However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.



Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.



And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.









-- 

https://bugs.ruby-lang.org/

 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116405] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (5 preceding siblings ...)
  2024-01-24 11:11 ` [ruby-core:116404] " duerst via ruby-core
@ 2024-01-24 11:12 ` Eregon (Benoit Daloze) via ruby-core
  2024-01-24 11:14 ` [ruby-core:116406] " byroot (Jean Boussier) via ruby-core
                   ` (44 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-01-24 11:12 UTC (permalink / raw
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20205 has been updated by Eregon (Benoit Daloze).


+1, sounds like a good plan.
And agreed with @zverok, having frozen literals by default is not only faster and less wasteful in memory usage, but also a safer model, where "String used like buffers/mutated Strings" are more explicit, which certainly seems good for readability and avoiding to accidentally mutate a String which should not be mutated.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106426

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116406] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (6 preceding siblings ...)
  2024-01-24 11:12 ` [ruby-core:116405] " Eregon (Benoit Daloze) via ruby-core
@ 2024-01-24 11:14 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 14:22 ` [ruby-core:116410] " mame (Yusuke Endoh) via ruby-core
                   ` (43 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 11:14 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> Maybe what could help is a declaration on a higher level, e.g. per gem or so rather than per source file

That's my fallback proposal if this one doesn't go through. Devise a way to set compile options for all files inside a directory, and then integrate with Rubygems to enable frozen string literals on a per gem basis. 

But I'd prefer to just flip the default if possible.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106427

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116410] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (7 preceding siblings ...)
  2024-01-24 11:14 ` [ruby-core:116406] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 14:22 ` mame (Yusuke Endoh) via ruby-core
  2024-01-24 15:34 ` [ruby-core:116411] " byroot (Jean Boussier) via ruby-core
                   ` (42 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: mame (Yusuke Endoh) via ruby-core @ 2024-01-24 14:22 UTC (permalink / raw
  To: ruby-core; +Cc: mame (Yusuke Endoh)

Issue #20205 has been updated by mame (Yusuke Endoh).


@byroot Thanks for the quick benchmark! It depends on further benchmarking, but currently, I don't see enough advantage over compatibility. Do you think it is worth the risk of compatibility?

@zverok Ah, that's exactly why I was against the introduction of `frozen_string_literal`, because people misunderstand it like you. This feature must not be mixed up with "immutability". I can understand if all String objects were frozen by default, but freezing only string "literals" makes no sense at all (except in terms of performance). Consider, just adding `.upcase` or `.b` or something to a String literal makes it mutable. I don't find "a cultural thing" in such a fragile thing.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106432

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116411] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (8 preceding siblings ...)
  2024-01-24 14:22 ` [ruby-core:116410] " mame (Yusuke Endoh) via ruby-core
@ 2024-01-24 15:34 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 16:28 ` [ruby-core:116412] " jeremyevans0 (Jeremy Evans) via ruby-core
                   ` (41 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 15:34 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


I'm still trying to get a hold onto the benchmarking server, but the 5% figure on `lobsters` seems to be quite consistent on my machine, so I think I can use it for my argumentation. 

>  It depends on further benchmarking, but currently, I don't see enough advantage over compatibility. Do you think it is worth the risk of compatibility?

Alright, so I fear my answer to this will be longer than expected.

So I'm of course biased because the type of workload I work with are very heavily string based (Web), so for me, a 5% improvement (to be confirmed) is quite significant.

And also because while we have many dependencies (over 700 transitive gems in the monolith), we do make sure to prune or replace the abandoned ones, and have the habit to contribute to them regularly. So I'm quite confident we can get all our dependencies ready for this change without much work and in short order, It will certainly be much less work than the Ruby 2.7 keyword argument change was.

Now of course, for Ruby users that don't generally deal with Strings much, this is just yet another annoying change that don't give them anything substantial.

But also retaining compatibility for them is really trivial. If you are running a legacy code base or outdated dependency, but yet are upgrading to a newer Ruby, is it really that much work to just set `RUBYOPT="--disable=frozen_string_literal"` an move on? Especially after multiple versions warning you that you'll have to do it at some point? Is that legacy code even still working on 3.x after the keyword argument change?

Maybe my bias cause me to downplay the compatibility concern, but it really doesn't seem significant to me assuming a reasonably long timeline so the ecosystem have time to prepare and catch up.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106433

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116412] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (9 preceding siblings ...)
  2024-01-24 15:34 ` [ruby-core:116411] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 16:28 ` jeremyevans0 (Jeremy Evans) via ruby-core
  2024-01-24 17:14 ` [ruby-core:116414] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (40 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: jeremyevans0 (Jeremy Evans) via ruby-core @ 2024-01-24 16:28 UTC (permalink / raw
  To: ruby-core; +Cc: jeremyevans0 (Jeremy Evans)

Issue #20205 has been updated by jeremyevans0 (Jeremy Evans).


byroot (Jean Boussier) wrote in #note-10:
> Is that legacy code even still working on 3.x after the keyword argument change?

It depends on what you consider legacy code. Legacy code designed for Ruby 1.8 or 1.9, which never used keyword arguments, was unaffected by the keyword argument changes in Ruby 3.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106434

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116414] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (10 preceding siblings ...)
  2024-01-24 16:28 ` [ruby-core:116412] " jeremyevans0 (Jeremy Evans) via ruby-core
@ 2024-01-24 17:14 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-01-24 17:29 ` [ruby-core:116416] " rubyFeedback (robert heiler) via ruby-core
                   ` (39 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-01-24 17:14 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


> I think the community pulled through it and I don't seem to hear much about it anymore.

Careful; the community "pulled through" because there was already a lot of accumulated good will, and the 2.7 migration burned through some of that reserve. Yet another migration might result in "not again!" syndrome and the community not pulling through nearly as well. It depends on how annoying the migration is, and the perceived benefit.

I should note that since dynamic string literals are no longer frozen since 3.0, the disruption would be that much smaller.

> - Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
> - This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

That's a lot like #16153 so I like it. I would also like to have `# frozen_string_literal: chilled` to enable this behavior on my own terms, without impacting gems over which I have no control.

> learning from the keyword argument warning experience

I think another important lesson from that experience is that gems are different from app code. If I want to optimize my app to use frozen string literals, I have to enable this warning at the global level, and then if warnings from gems are mixed in it makes my job a lot more annoying. For all kinds of reasons I do not want to update my apps and gems at the same time.

> Maybe what could help is a declaration on a higher level, e.g. per gem or so rather than per source file

It's a bit similar to #17156 so I like it. Actually I would much prefer this than changing the default; it allows every app and gem to upgrade on their own terms, without enforcing a one-size-fits-all default, and without the noise of a pragma in every file. Especially if combined with `frozen_string_literal: chilled` it would be very empowering.

> But also retaining compatibility for them is really trivial. If you are running a legacy code base or outdated dependency, but yet are upgrading to a newer Ruby, is it really that much work to just set `RUBYOPT="--disable=frozen_string_literal"` an move on?

Conversely let me ask: Is it really that much work to just set `RUBYOPT="--enable=frozen_string_literal"` in your application instead of forcing a new default on everyone else?


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106437

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116416] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (11 preceding siblings ...)
  2024-01-24 17:14 ` [ruby-core:116414] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-01-24 17:29 ` rubyFeedback (robert heiler) via ruby-core
  2024-01-24 17:46 ` [ruby-core:116417] " byroot (Jean Boussier) via ruby-core
                   ` (38 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: rubyFeedback (robert heiler) via ruby-core @ 2024-01-24 17:29 UTC (permalink / raw
  To: ruby-core; +Cc: rubyFeedback (robert heiler)

Issue #20205 has been updated by rubyFeedback (robert heiler).


I think this was discussed or suggested before. Personally
I have all my .rb files with one header line being:

    # frozen_string_literal: true

so a change towards frozen string literals would not affect
me, as I already use that by default, since many years
actually.

So the question, then, is, how this may affect other ruby
users and developers.

> One example of that was the Ruby 2.7 keyword argument
> deprecation. It was quite verbose, and some users were
> initially annoyed, but I think the community pulled
> through it and I don't seem to hear much about it anymore.

This is not quite how I remember it; I think there were tons
of warnings initially, in particular from rails code bases,
and some changes were made to the warning situation.

zverok wrote:






----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106439

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116417] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (12 preceding siblings ...)
  2024-01-24 17:29 ` [ruby-core:116416] " rubyFeedback (robert heiler) via ruby-core
@ 2024-01-24 17:46 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 17:46 ` [ruby-core:116418] " palkan (Vladimir Dementyev) via ruby-core
                   ` (37 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 17:46 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> Conversely let me ask: Is it really that much work to just set RUBYOPT="--enable=frozen_string_literal" in your application instead of forcing a new default on everyone else?

The thing is, I can't.

Code that is written for `frozen_string_literal: true` can generally run with `frozen_string_literal: false`, but the opposite is not true.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106440

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116418] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (13 preceding siblings ...)
  2024-01-24 17:46 ` [ruby-core:116417] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 17:46 ` palkan (Vladimir Dementyev) via ruby-core
  2024-01-24 18:13 ` [ruby-core:116419] " byroot (Jean Boussier) via ruby-core
                   ` (36 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: palkan (Vladimir Dementyev) via ruby-core @ 2024-01-24 17:46 UTC (permalink / raw
  To: ruby-core; +Cc: palkan (Vladimir Dementyev)

Issue #20205 has been updated by palkan (Vladimir Dementyev).


byroot (Jean Boussier) wrote in #note-8:

>  Devise a way to set compile options for all files inside a directory

Here you go: https://github.com/ruby-next/freezolite 🙂

> integrate with Rubygems to enable frozen string literals on a per gem basis

Smth like `spec.frozen_string_literals = true`? And during gem registration (setting up a load path), RubyGems can add the path to the _frozen list_, so upon load we can set the compile option. That should work, I think.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106441

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116419] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (14 preceding siblings ...)
  2024-01-24 17:46 ` [ruby-core:116418] " palkan (Vladimir Dementyev) via ruby-core
@ 2024-01-24 18:13 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 18:53 ` [ruby-core:116420] " tenderlovemaking (Aaron Patterson) via ruby-core
                   ` (35 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 18:13 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> Here you go: https://github.com/ruby-next/freezolite 🙂

As mentioned on Reddit when you first published that gem, it's a nice Hack, but I don't think it's quite robust enough. If changing the default isn't accepted and instead we try to make it a per gem configuration, I think Ruby will need to expose a better API to do this in a more reliable and clean way.



----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106442

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116420] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (15 preceding siblings ...)
  2024-01-24 18:13 ` [ruby-core:116419] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 18:53 ` tenderlovemaking (Aaron Patterson) via ruby-core
  2024-01-24 18:56 ` [ruby-core:116421] " tenderlovemaking (Aaron Patterson) via ruby-core
                   ` (34 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: tenderlovemaking (Aaron Patterson) via ruby-core @ 2024-01-24 18:53 UTC (permalink / raw
  To: ruby-core; +Cc: tenderlovemaking (Aaron Patterson)

Issue #20205 has been updated by tenderlovemaking (Aaron Patterson).


mame (Yusuke Endoh) wrote in #note-9:
> I can understand if all String objects were frozen by default, but freezing only string "literals" makes no sense at all (except in terms of performance).

For me, freezing string literals is a useful way to catch bugs. When I mutate strings, I am trying to be intentional, purposeful, and isolated.

```ruby
buf = "".b # I intend to mutate this string
```

```ruby
name = "Aaron" # I don't intend to mutate this, if I do, it's a bug
```

The places in code where I intend to mutate a string are infrequent and isolated so I am happy to pay a ".dup" tax in order to avoid bugs in other parts of my code.  Maybe we could freeze all strings by default in the future, but when we're on the subject of compatibility, I think doing that right now would be far too extreme. Freezing string literals buys us performance (as byroot says, even 5% is great), and _some_ safety. The trade is compatibility, but I don't think the trade is worthwhile and not very extreme.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106443

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116421] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (16 preceding siblings ...)
  2024-01-24 18:53 ` [ruby-core:116420] " tenderlovemaking (Aaron Patterson) via ruby-core
@ 2024-01-24 18:56 ` tenderlovemaking (Aaron Patterson) via ruby-core
  2024-01-24 19:42 ` [ruby-core:116422] " palkan (Vladimir Dementyev) via ruby-core
                   ` (33 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: tenderlovemaking (Aaron Patterson) via ruby-core @ 2024-01-24 18:56 UTC (permalink / raw
  To: ruby-core; +Cc: tenderlovemaking (Aaron Patterson)

Issue #20205 has been updated by tenderlovemaking (Aaron Patterson).


> The trade is compatibility, but I don't think the trade is worthwhile and not very extreme.

Sorry, I made a typo. I _do_ think the trade is worthwhile, and I _don't_ think it's very extreme.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106444

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116422] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (17 preceding siblings ...)
  2024-01-24 18:56 ` [ruby-core:116421] " tenderlovemaking (Aaron Patterson) via ruby-core
@ 2024-01-24 19:42 ` palkan (Vladimir Dementyev) via ruby-core
  2024-01-24 19:45 ` [ruby-core:116423] " palkan (Vladimir Dementyev) via ruby-core
                   ` (32 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: palkan (Vladimir Dementyev) via ruby-core @ 2024-01-24 19:42 UTC (permalink / raw
  To: ruby-core; +Cc: palkan (Vladimir Dementyev)

Issue #20205 has been updated by palkan (Vladimir Dementyev).


byroot (Jean Boussier) wrote in #note-16:
> > Here you go: https://github.com/ruby-next/freezolite 🙂
> 
> As mentioned on Reddit when you first published that gem, it's a nice Hack, but I don't think it's quite robust enough. If changing the default isn't accepted and instead we try to make it a per gem configuration, I think Ruby will need to expose a better API to do this in a more reliable and clean way.

Sure, it must be a part of MRI (and other implementations). Consider it a PoC (though, quite robust and battle-tested in production) and example of how to approach path-based compilation settings. The most important thing here is an API to _wrap_ code loading so we can adjust settings on-the-fly (smth like [require-hooks](https://github.com/ruby-next/require-hooks)).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106445

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116423] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (18 preceding siblings ...)
  2024-01-24 19:42 ` [ruby-core:116422] " palkan (Vladimir Dementyev) via ruby-core
@ 2024-01-24 19:45 ` palkan (Vladimir Dementyev) via ruby-core
  2024-01-24 19:48 ` [ruby-core:116424] " kddnewton (Kevin Newton) via ruby-core
                   ` (31 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: palkan (Vladimir Dementyev) via ruby-core @ 2024-01-24 19:45 UTC (permalink / raw
  To: ruby-core; +Cc: palkan (Vladimir Dementyev)

Issue #20205 has been updated by palkan (Vladimir Dementyev).


> Files with # frozen_string_literal: true or # frozen_string_literal: false don't change in behavior at all.

There is one use case in which having `# frozen_string_literal: true` differs from `RUBYOPT=--enable=frozen_string_literal` today:

```ruby
# frozen_string_literal: true

class B
  attr_reader :name

  def initialize
    @name = "B"
  end

  def eval_name
    instance_eval '@name = "C"'
  end
end

b = B.new

puts b.name.frozen?

b.eval_name

puts b.name.frozen?
```

Results in:

```sh
$ ruby -v
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin21]

$ ruby b.rb

true
false

$ RUBYOPT='--enable=frozen_string_literal' ruby b.rb

true
true
```




----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106446

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116424] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (19 preceding siblings ...)
  2024-01-24 19:45 ` [ruby-core:116423] " palkan (Vladimir Dementyev) via ruby-core
@ 2024-01-24 19:48 ` kddnewton (Kevin Newton) via ruby-core
  2024-01-24 19:49 ` [ruby-core:116425] " byroot (Jean Boussier) via ruby-core
                   ` (30 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: kddnewton (Kevin Newton) via ruby-core @ 2024-01-24 19:48 UTC (permalink / raw
  To: ruby-core; +Cc: kddnewton (Kevin Newton)

Issue #20205 has been updated by kddnewton (Kevin Newton).


> There is one use case in which having # frozen_string_literal: true differs from RUBYOPT=--enable=frozen_string_literal today

I don't think that's a difference, you don't have the magic comment in the eval. Changing it to:

``` ruby
instance_eval "# frozen_string_literal: true\n@name = \"C\""
```

makes them both `true`.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106447

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116425] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (20 preceding siblings ...)
  2024-01-24 19:48 ` [ruby-core:116424] " kddnewton (Kevin Newton) via ruby-core
@ 2024-01-24 19:49 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 19:57 ` [ruby-core:116426] " palkan (Vladimir Dementyev) via ruby-core
                   ` (29 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 19:49 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


That's by design, each `eval` call is its own file, and it doesn't inherit the pragma from the file where it's invoked, which is logical if you think about it.

But you are right that it is uncommon for users to put `# frozen_string_literal: ` pragmas in evaled code, so this could indeed require some small adjustment for codebases that already define a pragma, but it should be very rare.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106448

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116426] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (21 preceding siblings ...)
  2024-01-24 19:49 ` [ruby-core:116425] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 19:57 ` palkan (Vladimir Dementyev) via ruby-core
  2024-01-24 21:36 ` [ruby-core:116427] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (28 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: palkan (Vladimir Dementyev) via ruby-core @ 2024-01-24 19:57 UTC (permalink / raw
  To: ruby-core; +Cc: palkan (Vladimir Dementyev)

Issue #20205 has been updated by palkan (Vladimir Dementyev).


byroot (Jean Boussier) wrote in #note-22:
> But you are right that it is uncommon for users to put `# frozen_string_literal: ` pragmas in evaled code, so this could indeed require some small adjustment for codebases that already define a pragma, but it should be very rare.

Yeah, that's what I meant. Even if users put `# frozen_string_literal: ` in every file (but not within eval) they still might need to adjust their code. So, I'd suggest covering this edge case in the migration guide (or whatever).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106449

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116427] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (22 preceding siblings ...)
  2024-01-24 19:57 ` [ruby-core:116426] " palkan (Vladimir Dementyev) via ruby-core
@ 2024-01-24 21:36 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-01-24 21:58 ` [ruby-core:116428] " byroot (Jean Boussier) via ruby-core
                   ` (27 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-01-24 21:36 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


byroot (Jean Boussier) wrote in #note-14:
> > Conversely let me ask: Is it really that much work to just set RUBYOPT="--enable=frozen_string_literal" in your application instead of forcing a new default on everyone else?
> 
> The thing is, I can't.

Sorry, that didn't make sense to me... with a new ruby version having frozen_string_literal enabled by default you are "quite confident we can get all our dependencies ready", and yet with `--enable=frozen_string_literal` which has exactly the same effect, you "can't" ??? It seems to me the ideal course here is you test your 700 gems with `--enable=frozen_string_literal` then submit any fixes to the gems' maintainers, and possiblity convince them to switch to `frozen_string_literal: false` style, and then you can run your app with `--enable=frozen_string_literal` and everyone's happy. Doesn't seem to be any need to change the default of the ruby interpreter for that.

> Code that is written for `frozen_string_literal: true` can generally run with `frozen_string_literal: false`, but the opposite is not true.

Indeed, and that's why changing the current default of false to true runs the risk of incompatibility. If you consider that a problem that prevents you from using `--enable=frozen_string_literal`, why would it not be a problem when changing the interpreter default?

I mean, of course what's missing in the above is the "chilled string" of the proposal, which provides a feasible migration path. But having these chilled strings is orthogonal to changing the default. You could run your app with e.g. `--enable=CHILLED_string_literal` and achieve your goal without having to change the default.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106450

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116428] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (23 preceding siblings ...)
  2024-01-24 21:36 ` [ruby-core:116427] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-01-24 21:58 ` byroot (Jean Boussier) via ruby-core
  2024-01-24 22:25 ` [ruby-core:116429] " jeremyevans0 (Jeremy Evans) via ruby-core
                   ` (26 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 21:58 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


>  and possiblity convince them to switch to frozen_string_literal: false style

You are missing the social aspect of it.

As of today running ruby with `--enable=frozen_string_literal` is a little known feature that basically no-one is doing. It's hard, if not impossible to convince maintainers to take in such changes. Since it's not the default, it basically comes down to personal preference (cf `standardrb`).

And any new gem created from today will likely not test with `--enable=frozen_string_literal`, so when they get added on our apps, they won't work. So it will be a never ending task.

Now if the Ruby project states that in the future the default will flip, even if it's a long time from now, it becomes easy to convince maintainers.

I'm not asking to change the default because I'm too lazy to fix some gems. I'm more than happy to help gems upgrade, I fixed over a hundred gems to be compatible with Ruby 3.0..., and will likely fix many more to be compatible with frozen string literals whenever Ruby decide to make the switch.

This is just me recognizing I can't realistically make this change alone in my corner of the ecosystem, no matter how much effort I put in it.

But also, I'm not getting this out of thin air, unless I misunderstood Matz, he stated he wishes to enable frozen string literals by default at some point, and that the only reason it wasn't done yet is the lack of a proper migration plan. So I'm not the one to suggest to flip the default in the first place, I'm merely proposing a migration plan.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106451

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116429] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (24 preceding siblings ...)
  2024-01-24 21:58 ` [ruby-core:116428] " byroot (Jean Boussier) via ruby-core
@ 2024-01-24 22:25 ` jeremyevans0 (Jeremy Evans) via ruby-core
  2024-01-24 22:32 ` [ruby-core:116430] " byroot (Jean Boussier) via ruby-core
                   ` (25 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: jeremyevans0 (Jeremy Evans) via ruby-core @ 2024-01-24 22:25 UTC (permalink / raw
  To: ruby-core; +Cc: jeremyevans0 (Jeremy Evans)

Issue #20205 has been updated by jeremyevans0 (Jeremy Evans).


byroot (Jean Boussier) wrote in #note-25:
> As of today running ruby with `--enable=frozen_string_literal` is a little known feature that basically no-one is doing. It's hard, if not impossible to convince maintainers to take in such changes.

As a counterpoint, I've been testing Sequel, Roda, and Rodauth since the release of Ruby 2.3 with `--enable-frozen-string-literal`.  When starting out, many of the dependencies (direct and transitive) broke internally, and I had to submit pull requests to fix them.  It took a few years, but I eventually got all related pull requests merged upstream (or alternative fixes implemented), and for years the tests have been clean with `--enable-frozen-string-literal`.

In most cases, it was easy to convince maintainers to fix any breakage encountered when using `--enable-frozen-string-literal`, and in many cases, maintainers took it upon themselves to fix other issues I didn't encounter.

> And any new gem created from today will likely not test with `--enable=frozen_string_literal`, so when they get added on our apps, they won't work. So it will be a never ending task.

It's been years since I've had to submit a pull request upstream related to `--enable-frozen-string-literal`, so I don't think it necessarily has to be a never ending task.

However, I'm not dealing with 700 transitive dependencies, maybe not even 70.  And when you are using `--enable-frozen-string-literal`, you need to have everything fixed for things to work.  So maybe at Shopify scale, the problem really is intractable.  

> Now if the Ruby project states that in the future the default will flip, even if it's a long time from now, it becomes easy to convince maintainers.

Certainly it becomes easier, but my experience is that it is already easy.  I would expect you are more likely to run into a dependency that isn't maintained, versus a dependency that is maintained but the maintainer is against fixing `--enable-frozen-string-literal` issues.

> But also, I'm not getting this out of thin air, unless I misunderstood Matz, he stated he wishes to enable frozen string literals by default at some point, and that the only reason it wasn't done yet is the lack of a proper migration plan. So I'm not the one to suggest to flip the default in the first place, I'm merely proposing a migration plan.

I am in favor of switching to frozen static string literals by default with the migration plan proposed.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106452

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116430] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (25 preceding siblings ...)
  2024-01-24 22:25 ` [ruby-core:116429] " jeremyevans0 (Jeremy Evans) via ruby-core
@ 2024-01-24 22:32 ` byroot (Jean Boussier) via ruby-core
  2024-01-25 11:30 ` [ruby-core:116442] " Eregon (Benoit Daloze) via ruby-core
                   ` (24 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-24 22:32 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> when you are using --enable-frozen-string-literal, you need to have everything fixed for things to work. So maybe at Shopify scale, the problem really is intractable.

Yes you are right, I should have said, it's hard if not impossible to convince **all** the maintainers of my transitive dependencies.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106453

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116442] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (26 preceding siblings ...)
  2024-01-24 22:32 ` [ruby-core:116430] " byroot (Jean Boussier) via ruby-core
@ 2024-01-25 11:30 ` Eregon (Benoit Daloze) via ruby-core
  2024-01-25 12:18 ` [ruby-core:116443] " byroot (Jean Boussier) via ruby-core
                   ` (23 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-01-25 11:30 UTC (permalink / raw
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20205 has been updated by Eregon (Benoit Daloze).


mame (Yusuke Endoh) wrote in #note-9:
> @zverok Ah, that's exactly why I was against the introduction of `frozen_string_literal`, because people misunderstand it like you. This feature must not be mixed up with "immutability". I can understand if all String objects were frozen by default, but freezing only string "literals" makes no sense at all (except in terms of performance). Consider, just adding `.upcase` or `.b` or something to a String literal makes it mutable. I don't find "a cultural thing" in such a fragile thing.

It is not full immutability of all Strings, true.
But it is full immutability of all (static) String literals. And that is valuable. With the new default it becomes impossible to accidentally mutate a String literal, which is a nice error category to remove.
It also saves some memory because the same String literal in different places is the same String object.
If not frozen, the bytes can be shared but not the String object itself.

I would say 5% on lobsters is a huge gain.
Very few optimizations can give that much (and as an extra it's fairly simple and well understood semantically).

---

My impression is most Rubyists are aware that basically all new code should use `# frozen_string_literal: true` semantics.
The default of `false` is basically deprecated in practice and has almost no value, except compatibility for existing code.

As an example very very few files use `# frozen_string_literal: false`.
And those that do typically only do it because they have no been migrated to `# frozen_string_literal: true` and to not break under `--enable-frozen-string-literal`.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106466

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116443] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (27 preceding siblings ...)
  2024-01-25 11:30 ` [ruby-core:116442] " Eregon (Benoit Daloze) via ruby-core
@ 2024-01-25 12:18 ` byroot (Jean Boussier) via ruby-core
  2024-01-25 13:53 ` [ruby-core:116444] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (22 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-25 12:18 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> I would say 5% on lobsters is a huge gain.

Still trying to get hold on our benchmarking server...

But just to note, `lobsters` itself doesn't use `frozen_string_literal: true`, and it's probably the case of at least some of its dependencies too.

When I have some extra time I'd also like to make it `--enable=frozen_string_liteal` compatible to see if a couple more % could be squeezed out of it.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106467

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116444] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (28 preceding siblings ...)
  2024-01-25 12:18 ` [ruby-core:116443] " byroot (Jean Boussier) via ruby-core
@ 2024-01-25 13:53 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-01-25 14:33 ` [ruby-core:116445] " byroot (Jean Boussier) via ruby-core
                   ` (21 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-01-25 13:53 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


> I would say 5% on lobsters is a huge gain.

Let's not cherry-pick here. 5% is the best case. The worst case is -6% on setivar_object. And something more relevant to a rails application: erubi has -3%. The average of all tests is roughly 0%. So it's not like the performance benefit is clearly compelling. At least based on these synthetic benchmarks.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106468

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116445] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (29 preceding siblings ...)
  2024-01-25 13:53 ` [ruby-core:116444] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-01-25 14:33 ` byroot (Jean Boussier) via ruby-core
  2024-01-25 15:32 ` [ruby-core:116446] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (20 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-25 14:33 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> The worst case is -6% on setivar_object

This is a fluke caused by random slowdown on my development machine. Here's the benchmark source: https://github.com/Shopify/yjit-bench/blob/3774b4bc320519f8b560eb23bdea48f549cf8b30/benchmarks/setivar_object.rb

There is absolutely nothing in there influenced by frozen strings. I'm somewhat confident on the 5% figure for lobsters because I ran it alone about 10 times and always got 5%. But running the full suite on my machine takes way too long to do that for all benchmarks.

I shouldn't have ran the micro-benchmarks anyway, only the headline benchmarks make sense.

What is certain is that turning on `frozen_string_literal` cannot possibly have a negative performance impact. Only null or positive. So don't lean to much on these preliminary results.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106469

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116446] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (30 preceding siblings ...)
  2024-01-25 14:33 ` [ruby-core:116445] " byroot (Jean Boussier) via ruby-core
@ 2024-01-25 15:32 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-01-25 15:43 ` [ruby-core:116447] " byroot (Jean Boussier) via ruby-core
                   ` (19 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-01-25 15:32 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


byroot (Jean Boussier) wrote in #note-31:
> I shouldn't have ran the micro-benchmarks anyway, only the headline benchmarks make sense.

I'll freely admit I have no idea which benchmarks are micro and which are headline. What about erubi and erubi-rails?

> What is certain is that turning on `frozen_string_literal` cannot possibly have a negative performance impact.

I wouldn't affirm "cannot possibly", but I tend to agree; though deduplication has an overhead, I'd be surprised if it was measurable. So the erubi -3% result stands out. Was that a benchmark glitch?

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106470

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116447] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (31 preceding siblings ...)
  2024-01-25 15:32 ` [ruby-core:116446] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-01-25 15:43 ` byroot (Jean Boussier) via ruby-core
  2024-01-25 16:49 ` [ruby-core:116449] " byroot (Jean Boussier) via ruby-core
                   ` (18 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-25 15:43 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


>  I have no idea which benchmarks are micro and which are headline. What about erubi and erubi-rails?

Each yjit-bench suite has a category: https://github.com/Shopify/yjit-bench/blob/3774b4bc320519f8b560eb23bdea48f549cf8b30/benchmarks.yml#L21

The more relevant ones are "headline", they generally are more sizeable and varied, so mor erepresentative of actually production workloads.

`erubi` and `erubi-rails` are both in the headline category. But `frozen_string_literal` doesn't matter one bit for `eruby` because `eruby` already compile into code that use frozen literals regardless.

> though deduplication has an overhead

`frozen_string_literal: true` doesn't incur any overhead even if it means you sometimes need to dup.

With `frozen_string_literal: false`, `"foo"` is strictly equivalent to `"foo".dup` except that the dup is done as part of `putstring` instead of a second instruction. But we could even eliminate that if we wanted by compiling `"literal".dup` into `putstring`. It is very very unlikely to make any measurable difference though.

So yes, if we want to be extremely pedantic it's possible to generate a micro-benchmark that would suffer from `frozen_string_literal: true`, but in reality the impact can't reasonably be negative.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106471

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116449] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (32 preceding siblings ...)
  2024-01-25 15:43 ` [ruby-core:116447] " byroot (Jean Boussier) via ruby-core
@ 2024-01-25 16:49 ` byroot (Jean Boussier) via ruby-core
  2024-02-14  7:30 ` [ruby-core:116733] " matz (Yukihiro Matsumoto) via ruby-core
                   ` (17 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-01-25 16:49 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


Alright, I finally got hold of the benchmarking server. For the record it's an AWS c5n.metal, with various tuning like disabling frequency scaling etc to hopefully get more stable results.

- `yjit-bench` revision: https://github.com/Shopify/yjit-bench/commit/95e1a3caddc7281fbaf5bd0f20b197561453993f
- `ruby` revision: https://github.com/Shopify/ruby/commit/bb0cee8daba4b70cedb40b36af05d796956475d6

Both rubyes are ran with YJIT enabled. `mutable` has `MUTABLE_STRINGS=1` which makes the `frozen_string_literal: true` comment become essentially a noop.

I ran headline benchmark twice for good measures, most of them seem to be consistent with 1% between the two runs, except for `ruby-lsp` which has widely inconsistent results (`+/-12%`??), and `hexapdf` too to some extent (`+/-4%`).

```
mutable: ruby 3.4.0dev (2024-01-24T08:24:16Z mutable-strings bb0cee8dab) +YJIT dev [x86_64-linux]
frozen: ruby 3.4.0dev (2024-01-24T08:24:16Z mutable-strings bb0cee8dab) +YJIT dev [x86_64-linux]

--------------  ------------  ----------  -----------  ----------  --------------  --------------
bench           mutable (ms)  stddev (%)  frozen (ms)  stddev (%)  frozen 1st itr  mutable/frozen
activerecord    72.8          1.2         70.8         1.2         1.03            1.03          
chunky-png      1666.3        0.1         1672.7       0.2         1.00            1.00          
erubi-rails     2330.1        0.3         2312.2       0.2         1.01            1.01          
hexapdf         3960.1        8.9         3614.7       7.3         1.04            1.10          
liquid-c        107.1         1.3         101.3        1.4         0.95            1.06          
liquid-compile  100.0         2.8         98.5         3.1         1.00            1.02          
liquid-render   153.0         1.1         137.8        1.1         0.97            1.11          
lobsters        1346.6        8.5         1239.2       7.6         1.00            1.09          
mail            202.8         0.8         198.9        1.0         1.02            1.02          
psych-load      3723.2        0.1         3661.3       0.1         1.01            1.02          
railsbench      2669.8        0.1         2519.6       0.2         1.02            1.06          
rubocop         278.6         5.9         272.5        6.1         1.00            1.02          
ruby-lsp        217.5         8.7         250.7        10.4        0.98            0.87          
sequel          111.8         0.7         111.8        0.8         1.01            1.00          
--------------  ------------  ----------  -----------  ----------  --------------  --------------
Legend:
- frozen 1st itr: ratio of mutable/frozen time for the first benchmarking iteration.
- mutable/frozen: ratio of mutable/frozen time. Higher is better for frozen. Above 1 represents a speedup.



mutable: ruby 3.4.0dev (2024-01-24T08:24:16Z mutable-strings bb0cee8dab) +YJIT dev [x86_64-linux]
frozen: ruby 3.4.0dev (2024-01-24T08:24:16Z mutable-strings bb0cee8dab) +YJIT dev [x86_64-linux]

--------------  ------------  ----------  -----------  ----------  --------------  --------------
bench           mutable (ms)  stddev (%)  frozen (ms)  stddev (%)  frozen 1st itr  mutable/frozen
activerecord    72.8          1.2         70.8         1.3         1.00            1.03          
chunky-png      1664.1        0.2         1672.5       0.2         1.01            0.99          
erubi-rails     2351.9        0.2         2311.4       0.2         1.01            1.02          
hexapdf         3759.9        7.0         3696.5       5.8         1.04            1.02          
liquid-c        108.0         1.1         101.3        1.4         0.97            1.07          
liquid-compile  100.5         3.1         98.5         3.1         0.99            1.02          
liquid-render   152.8         1.0         138.0        1.1         0.97            1.11          
lobsters        1339.0        9.8         1244.2       7.9         1.00            1.08          
mail            202.6         0.7         198.9        1.0         1.00            1.02          
psych-load      3627.0        0.1         3659.6       0.0         0.99            0.99          
railsbench      2670.7        0.1         2560.3       1.7         1.02            1.04          
rubocop         279.5         6.3         271.4        6.1         1.00            1.03          
ruby-lsp        274.2         9.5         245.0        9.8         0.98            1.12          
sequel          111.9         0.7         112.0        1.3         1.01            1.00          
--------------  ------------  ----------  -----------  ----------  --------------  --------------
Legend:
- frozen 1st itr: ratio of mutable/frozen time for the first benchmarking iteration.
- mutable/frozen: ratio of mutable/frozen time. Higher is better for frozen. Above 1 represents a speedup.
```

Overall the performance benefit seem much more important than on my initial benchmark, likely in part because YJIT is enabled and also likely in part because of the different architecture (`X86_64` vs `ARM64`).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106473

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116733] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (33 preceding siblings ...)
  2024-01-25 16:49 ` [ruby-core:116449] " byroot (Jean Boussier) via ruby-core
@ 2024-02-14  7:30 ` matz (Yukihiro Matsumoto) via ruby-core
  2024-02-14  9:08 ` [ruby-core:116735] " byroot (Jean Boussier) via ruby-core
                   ` (16 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: matz (Yukihiro Matsumoto) via ruby-core @ 2024-02-14  7:30 UTC (permalink / raw
  To: ruby-core; +Cc: matz (Yukihiro Matsumoto)

Issue #20205 has been updated by matz (Yukihiro Matsumoto).


I agree with the proposal. It seems a well-thought process to migrate. The performance improvement was not as great as I had hoped for. But since I feel that the style of individually freezing strings when setting them to constants is not beautiful, and since I feel that magic comment is not a good style. I feel that making string literals frozen is the right direction to go in the long run.

Matz.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106752

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116735] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (34 preceding siblings ...)
  2024-02-14  7:30 ` [ruby-core:116733] " matz (Yukihiro Matsumoto) via ruby-core
@ 2024-02-14  9:08 ` byroot (Jean Boussier) via ruby-core
  2024-02-14 14:48 ` [ruby-core:116753] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (15 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-02-14  9:08 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


Thank you Matz.

In that case I'll work with @etienne into getting the proof of concept into a mergeable feature over the next few weeks.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106754

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116753] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (35 preceding siblings ...)
  2024-02-14  9:08 ` [ruby-core:116735] " byroot (Jean Boussier) via ruby-core
@ 2024-02-14 14:48 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-02-14 16:28 ` [ruby-core:116759] " byroot (Jean Boussier) via ruby-core
                   ` (14 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-02-14 14:48 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


Question: what is the effect of "chilled string" on `#frozen?`
Does `chilled_string.frozen?` return true?
Personally I think it should, as I remarked in #16153#note-11

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106776

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:116759] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (36 preceding siblings ...)
  2024-02-14 14:48 ` [ruby-core:116753] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-02-14 16:28 ` byroot (Jean Boussier) via ruby-core
  2024-03-19 17:17 ` [ruby-core:117232] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (13 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-02-14 16:28 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> Question: what is the effect of "chilled string" on #frozen?
> Does chilled_string.frozen? return true?

That's the current idea yes.

```ruby
"chilled string".frozen? # => true
+"chilled string" # returns a new, mutable string (without chilled flag)
-"chilled string" # returns a different, frozen string
"chilled string".freeze # the chilled string become frozen for real.
"chilled string" << "foo" # emit a warning and clear the "chilled" status.
```

This way as you point out in the other issues, the common `dup if frozen?` idiom works as expected.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-106782

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117232] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (37 preceding siblings ...)
  2024-02-14 16:28 ` [ruby-core:116759] " byroot (Jean Boussier) via ruby-core
@ 2024-03-19 17:17 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-03-19 19:00 ` [ruby-core:117234] " byroot (Jean Boussier) via ruby-core
                   ` (12 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-03-19 17:17 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


Thank you for this great feature!

Would it be possible to have a ruby API for chilled strings? Something like `str.chill` or `String.chill(str)` ...

>   - `String#+@`: acts as if the string was mutable.

Nitpick: actually, `String#+@`: creates a dup as if the string was immutable.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-107320

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117234] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (38 preceding siblings ...)
  2024-03-19 17:17 ` [ruby-core:117232] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-03-19 19:00 ` byroot (Jean Boussier) via ruby-core
  2024-03-19 20:37 ` [ruby-core:117236] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (11 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-03-19 19:00 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


> Would it be possible to have a ruby API for chilled strings? Something like str.chill or String.chill(str)

It's technically very easy to implement, yes. Would need to be a separate feature request though.

Also one drawback is that right now chilled string are an internal concept that we'll be able to get rid of and cleanup in the future. If we expose it to users, we'll have to keep it forever. So it's debatable whether the benefit outweigh the maintenance burden long term.


----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-107321

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117236] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (39 preceding siblings ...)
  2024-03-19 19:00 ` [ruby-core:117234] " byroot (Jean Boussier) via ruby-core
@ 2024-03-19 20:37 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-03-23 19:32 ` [ruby-core:117299] " Eric Wong via ruby-core
                   ` (10 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-03-19 20:37 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


> it's debatable whether the benefit outweigh the maintenance burden long term.

Yeah, good point. And if truly necessary we could get a chilled string with `eval(str.inspect)` anyway.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-107323

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117299] Re: [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (40 preceding siblings ...)
  2024-03-19 20:37 ` [ruby-core:117236] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-03-23 19:32 ` Eric Wong via ruby-core
  2024-05-06 18:00 ` [ruby-core:117782] " headius (Charles Nutter) via ruby-core
                   ` (9 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Eric Wong via ruby-core @ 2024-03-23 19:32 UTC (permalink / raw
  To: ruby-core; +Cc: Eric Wong

"byroot (Jean Boussier) via ruby-core" <ruby-core@ml.ruby-lang.org> wrote:
> https://bugs.ruby-lang.org/issues/20205

> One example of that was the Ruby 2.7 keyword argument
> deprecation. It was quite verbose, and some users were
> initially annoyed, but I think the community pulled through it
> and I don't seem to hear much about it anymore.

Or the users gave up and quit Ruby given the constant churn :P
I know this happened all around me throughout the 2010s...

Anyways, I wrote some Perl the other week to add fsl:false somewhat
intelligently (accounting for other comment-pragma, shebang,
etc):

  # for old git users
  wget https://yhbt.net/add-fsl.git/74d7689a641eca02/s/add-fsl.perl

cloneable with recent-ish git for SHA256:
git clone https://yhbt.net/add-fsl.git # needs git 2.32+
git clone git://yhbt.net/add-fsl.git # needs git 2.2x or so...
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117782] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (41 preceding siblings ...)
  2024-03-23 19:32 ` [ruby-core:117299] " Eric Wong via ruby-core
@ 2024-05-06 18:00 ` headius (Charles Nutter) via ruby-core
  2024-05-06 18:58 ` [ruby-core:117784] " byroot (Jean Boussier) via ruby-core
                   ` (8 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: headius (Charles Nutter) via ruby-core @ 2024-05-06 18:00 UTC (permalink / raw
  To: ruby-core; +Cc: headius (Charles Nutter)

Issue #20205 has been updated by headius (Charles Nutter).


I am a bit late to the party but nobody seems to have raised a concern I have.

If a chilled string appears to be `frozen?` then a consumer may proceed to use the string expecting it to remain frozen, such as for a cache key. If that string can later become unfrozen and be modified, warning or not, they may now have a broken cache with an unexpected mutable key.

I don't know of a specific case for this, but the fact that chilled strings masquerade as frozen when they are not really frozen seems like a major issue to me.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108190

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117784] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (42 preceding siblings ...)
  2024-05-06 18:00 ` [ruby-core:117782] " headius (Charles Nutter) via ruby-core
@ 2024-05-06 18:58 ` byroot (Jean Boussier) via ruby-core
  2024-05-20  1:37 ` [ruby-core:117930] " byroot (Jean Boussier) via ruby-core
                   ` (7 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-05-06 18:58 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


While your concern is absolutely valid, I don't think it's much of a problem in practice. There is pretty much an infinite amount of code out there, so I don't think there is any way really to discount it, but after having migrated a gigantic codebase to 3.4-dev no such issue appeared.

And generally speaking, code that want to hold on hold on a frozen string like you mention tend to use either `str.dup.freeze`, or `str.freeze` or `-str`. `str.frozen? ? str : str.dup.freeze` isn't a very common pattern.

So yes it could happen, but you'd really need many stars to align for that.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108191

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117930] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (43 preceding siblings ...)
  2024-05-06 18:58 ` [ruby-core:117784] " byroot (Jean Boussier) via ruby-core
@ 2024-05-20  1:37 ` byroot (Jean Boussier) via ruby-core
  2024-05-23 17:53 ` [ruby-core:117988] " Dan0042 (Daniel DeLorme) via ruby-core
                   ` (6 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-05-20  1:37 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


I thought about this more at Kaigi, maybe avoiding the false positive on the `str.dup if str.frozen?` pattern isn't worth the possible confusion.

I'll experiment with chilled strings `frozen?` method returning `false`.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108342

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117988] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (44 preceding siblings ...)
  2024-05-20  1:37 ` [ruby-core:117930] " byroot (Jean Boussier) via ruby-core
@ 2024-05-23 17:53 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-05-23 18:00 ` [ruby-core:117989] " byroot (Jean Boussier) via ruby-core
                   ` (5 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-05-23 17:53 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


@byroot How is that experiment going? I'm all for experimenting, but just as in #15554 I believe we should reduce false positives to a minimum.

Also I spent some time on this, but I'm having a hard time coming up with a non-contrived example case where returning false for #frozen? is beneficial.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108412

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117989] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (45 preceding siblings ...)
  2024-05-23 17:53 ` [ruby-core:117988] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-05-23 18:00 ` byroot (Jean Boussier) via ruby-core
  2024-05-24 12:42 ` [ruby-core:117998] " Eregon (Benoit Daloze) via ruby-core
                   ` (4 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2024-05-23 18:00 UTC (permalink / raw
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #20205 has been updated by byroot (Jean Boussier).


I'm still on my way back from Kaigi, so I haven't started working on this. But I had a quick chat with Matz, and it wasn't clear to him that we went with `frozen? -> true`, and he was clear he expects `frozen? -> false`.

Also based on the reception I saw of the release notes in various places, it seems to have created quite a bit of confusion.

So yes, this will introduce some false positive, which is unfortunate, but I'll definitely change the behavior soon.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108413

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117998] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (46 preceding siblings ...)
  2024-05-23 18:00 ` [ruby-core:117989] " byroot (Jean Boussier) via ruby-core
@ 2024-05-24 12:42 ` Eregon (Benoit Daloze) via ruby-core
  2024-05-24 12:46 ` [ruby-core:117999] " Eregon (Benoit Daloze) via ruby-core
                   ` (3 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-05-24 12:42 UTC (permalink / raw
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20205 has been updated by Eregon (Benoit Daloze).


> avoiding the false positive on the `str.dup if str.frozen?` pattern

`+str` seems a good replacement for that pattern.
Besides, I would think it's pretty rare that it's OK to use that pattern, because it mutates a String that is not "owned" if it happens to not be frozen (e.g. it's typically not OK if it's an argument, and if intended then no need to check if frozen).

IOW, returning false for frozen? for chilled strings seems safer and I think will cause very few false positive warnings (in comparison to true positive chilled strings warnings).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108420

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:117999] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (47 preceding siblings ...)
  2024-05-24 12:42 ` [ruby-core:117998] " Eregon (Benoit Daloze) via ruby-core
@ 2024-05-24 12:46 ` Eregon (Benoit Daloze) via ruby-core
  2024-05-24 13:19 ` [ruby-core:118000] " Eregon (Benoit Daloze) via ruby-core
                   ` (2 subsequent siblings)
  51 siblings, 0 replies; 53+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-05-24 12:46 UTC (permalink / raw
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20205 has been updated by Eregon (Benoit Daloze).


@Dan0042 @headius mentioned a few above.
It also seems pretty bad that an object could `frozen? => true` and then become unfrozen, I think that alone could cause very tricky and serious bugs (e.g. it would break custom hash tables).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108421

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:118000] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (48 preceding siblings ...)
  2024-05-24 12:46 ` [ruby-core:117999] " Eregon (Benoit Daloze) via ruby-core
@ 2024-05-24 13:19 ` Eregon (Benoit Daloze) via ruby-core
  2024-05-24 21:04 ` [ruby-core:118009] " Dan0042 (Daniel DeLorme) via ruby-core
  2024-06-03 22:30 ` [ruby-core:118162] " hartator (Julien Khaleghy) via ruby-core
  51 siblings, 0 replies; 53+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2024-05-24 13:19 UTC (permalink / raw
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #20205 has been updated by Eregon (Benoit Daloze).


To give a concrete example, Hash would be broken if it calls frozen? for string keys. That's the case on Rubinius: https://github.com/rubinius/rubinius/blob/84368419a49767ef9549a5778812e5f54b6c6223/core/hash.rb#L54-L56
So the pattern of "safe frozen copy" `str = str.dup.freeze unless str.frozen?` would be broken if frozen? would return true for chilled strings (str would still be mutable).

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108422

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:118009] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (49 preceding siblings ...)
  2024-05-24 13:19 ` [ruby-core:118000] " Eregon (Benoit Daloze) via ruby-core
@ 2024-05-24 21:04 ` Dan0042 (Daniel DeLorme) via ruby-core
  2024-06-03 22:30 ` [ruby-core:118162] " hartator (Julien Khaleghy) via ruby-core
  51 siblings, 0 replies; 53+ messages in thread
From: Dan0042 (Daniel DeLorme) via ruby-core @ 2024-05-24 21:04 UTC (permalink / raw
  To: ruby-core; +Cc: Dan0042 (Daniel DeLorme)

Issue #20205 has been updated by Dan0042 (Daniel DeLorme).


I was going to say this is MRI, not Rubinius, but it turns out chilled strings actually have a bug in MRI when used as Hash keys
```ruby
k = "key"
h = {}
h[k] = 42
k << "!"  #warning: literal string will be frozen in the future
p h       #{"key!"=>42}
```
The hash key is definitely supposed to be frozen here, not just chilled.

----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108430

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [ruby-core:118162] [Ruby master Feature#20205] Enable `frozen_string_literal` by default
  2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
                   ` (50 preceding siblings ...)
  2024-05-24 21:04 ` [ruby-core:118009] " Dan0042 (Daniel DeLorme) via ruby-core
@ 2024-06-03 22:30 ` hartator (Julien Khaleghy) via ruby-core
  51 siblings, 0 replies; 53+ messages in thread
From: hartator (Julien Khaleghy) via ruby-core @ 2024-06-03 22:30 UTC (permalink / raw
  To: ruby-core; +Cc: hartator (Julien Khaleghy)

Issue #20205 has been updated by hartator (Julien Khaleghy).


I think making `# frozen_string_literal: true` the default is a bad idea.

If the main point is to make Ruby faster, IRL benchmarks so far have shown the reverse. `byroot`'s own initial benchmarks show regular strings being faster or as fast as frozen strings in 62.2% of libs that has been tested. I would be interested to see if *removing* `# frozen_string_literal: true` will actually make some libs faster.

If the main point is to avoid some kind of bugs, the reverse is also true. `# frozen_string_literal: true` can be a misdirection and introduces its own kind of bugs. IRL this has introduces stubble bugs in our SerpApi codebase, consider this file:

``` ruby
# frozen_string_literal: true

class Converter
  LANGUADE_CODES = {
    en: "English",
    es: "Spanish",
    jp: "Japanese"
  }
  def lower_case_version
    LANGUADE_CODES.transform_values!(&:downcase)
  end
end
```

The initial quick assessment is to feel safe that `LANGUADE_CODES` can't be modified and `lower_case_version` would have one of these behaviors:
- Raise an error
- Log a warning
- Code works, but `LANGUADE_CODES` is unmodified.
- Code works, but `LANGUADE_CODES` is modified for only this object instance.

However, none of the above is true.

Surprisingly for both junior and senior engineers, a `#lower_case_version` method call will silently modify `LANGUADE_CODES` for all Ruby threads:

```
[7] pry(main)> Converter::LANGUADE_CODES
=> {:en=>"english", :es=>"spanish", :jp=>"japanese"}
```

I am afraid we are making a more complex and inflexible Ruby (I do agree that `string << "a story,"` appending style is lovely and super useful) for unfortunately no tangible gain in code explicitness and correctness, and now even have introducing surprising new behaviors. I think it's easier to just assume all String objects are mutable and just allows flexible coding.



----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205#change-108600

* Author: byroot (Jean Boussier)
* Status: Closed
----------------------------------------
### Context

The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.

According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required. 

The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.

The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.

One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.

So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.

### Deprecation Warning Implementation

I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549

In short:

- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.

Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.

But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.

As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.

But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.

### Timeline

The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.

- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.

### Impact

Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.

And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867)),
the few that didn't migrate will likely be made compatible quickly.

The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.

### Workflow for library maintainers

As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.

Alternatively they can of course make their code compatible with frozen string literals.

Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.

### Workflow for application owners

For application owners, the workflow is the same than for libraries.

However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.

Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.

And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2024-06-03 22:31 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-23 15:26 [ruby-core:116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default byroot (Jean Boussier) via ruby-core
2024-01-23 18:32 ` [ruby-core:116386] " matheusrich (Matheus Richard) via ruby-core
2024-01-24  4:38 ` [ruby-core:116390] " mame (Yusuke Endoh) via ruby-core
2024-01-24  9:34 ` [ruby-core:116396] " zverok (Victor Shepelev) via ruby-core
2024-01-24  9:44 ` [ruby-core:116397] " byroot (Jean Boussier) via ruby-core
2024-01-24  9:46 ` [ruby-core:116398] " byroot (Jean Boussier) via ruby-core
2024-01-24 11:11 ` [ruby-core:116404] " duerst via ruby-core
2024-01-24 11:12 ` [ruby-core:116405] " Eregon (Benoit Daloze) via ruby-core
2024-01-24 11:14 ` [ruby-core:116406] " byroot (Jean Boussier) via ruby-core
2024-01-24 14:22 ` [ruby-core:116410] " mame (Yusuke Endoh) via ruby-core
2024-01-24 15:34 ` [ruby-core:116411] " byroot (Jean Boussier) via ruby-core
2024-01-24 16:28 ` [ruby-core:116412] " jeremyevans0 (Jeremy Evans) via ruby-core
2024-01-24 17:14 ` [ruby-core:116414] " Dan0042 (Daniel DeLorme) via ruby-core
2024-01-24 17:29 ` [ruby-core:116416] " rubyFeedback (robert heiler) via ruby-core
2024-01-24 17:46 ` [ruby-core:116417] " byroot (Jean Boussier) via ruby-core
2024-01-24 17:46 ` [ruby-core:116418] " palkan (Vladimir Dementyev) via ruby-core
2024-01-24 18:13 ` [ruby-core:116419] " byroot (Jean Boussier) via ruby-core
2024-01-24 18:53 ` [ruby-core:116420] " tenderlovemaking (Aaron Patterson) via ruby-core
2024-01-24 18:56 ` [ruby-core:116421] " tenderlovemaking (Aaron Patterson) via ruby-core
2024-01-24 19:42 ` [ruby-core:116422] " palkan (Vladimir Dementyev) via ruby-core
2024-01-24 19:45 ` [ruby-core:116423] " palkan (Vladimir Dementyev) via ruby-core
2024-01-24 19:48 ` [ruby-core:116424] " kddnewton (Kevin Newton) via ruby-core
2024-01-24 19:49 ` [ruby-core:116425] " byroot (Jean Boussier) via ruby-core
2024-01-24 19:57 ` [ruby-core:116426] " palkan (Vladimir Dementyev) via ruby-core
2024-01-24 21:36 ` [ruby-core:116427] " Dan0042 (Daniel DeLorme) via ruby-core
2024-01-24 21:58 ` [ruby-core:116428] " byroot (Jean Boussier) via ruby-core
2024-01-24 22:25 ` [ruby-core:116429] " jeremyevans0 (Jeremy Evans) via ruby-core
2024-01-24 22:32 ` [ruby-core:116430] " byroot (Jean Boussier) via ruby-core
2024-01-25 11:30 ` [ruby-core:116442] " Eregon (Benoit Daloze) via ruby-core
2024-01-25 12:18 ` [ruby-core:116443] " byroot (Jean Boussier) via ruby-core
2024-01-25 13:53 ` [ruby-core:116444] " Dan0042 (Daniel DeLorme) via ruby-core
2024-01-25 14:33 ` [ruby-core:116445] " byroot (Jean Boussier) via ruby-core
2024-01-25 15:32 ` [ruby-core:116446] " Dan0042 (Daniel DeLorme) via ruby-core
2024-01-25 15:43 ` [ruby-core:116447] " byroot (Jean Boussier) via ruby-core
2024-01-25 16:49 ` [ruby-core:116449] " byroot (Jean Boussier) via ruby-core
2024-02-14  7:30 ` [ruby-core:116733] " matz (Yukihiro Matsumoto) via ruby-core
2024-02-14  9:08 ` [ruby-core:116735] " byroot (Jean Boussier) via ruby-core
2024-02-14 14:48 ` [ruby-core:116753] " Dan0042 (Daniel DeLorme) via ruby-core
2024-02-14 16:28 ` [ruby-core:116759] " byroot (Jean Boussier) via ruby-core
2024-03-19 17:17 ` [ruby-core:117232] " Dan0042 (Daniel DeLorme) via ruby-core
2024-03-19 19:00 ` [ruby-core:117234] " byroot (Jean Boussier) via ruby-core
2024-03-19 20:37 ` [ruby-core:117236] " Dan0042 (Daniel DeLorme) via ruby-core
2024-03-23 19:32 ` [ruby-core:117299] " Eric Wong via ruby-core
2024-05-06 18:00 ` [ruby-core:117782] " headius (Charles Nutter) via ruby-core
2024-05-06 18:58 ` [ruby-core:117784] " byroot (Jean Boussier) via ruby-core
2024-05-20  1:37 ` [ruby-core:117930] " byroot (Jean Boussier) via ruby-core
2024-05-23 17:53 ` [ruby-core:117988] " Dan0042 (Daniel DeLorme) via ruby-core
2024-05-23 18:00 ` [ruby-core:117989] " byroot (Jean Boussier) via ruby-core
2024-05-24 12:42 ` [ruby-core:117998] " Eregon (Benoit Daloze) via ruby-core
2024-05-24 12:46 ` [ruby-core:117999] " Eregon (Benoit Daloze) via ruby-core
2024-05-24 13:19 ` [ruby-core:118000] " Eregon (Benoit Daloze) via ruby-core
2024-05-24 21:04 ` [ruby-core:118009] " Dan0042 (Daniel DeLorme) via ruby-core
2024-06-03 22:30 ` [ruby-core:118162] " hartator (Julien Khaleghy) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).