RFC Trigger a copy when copy=False and X is read-only

Highly related to #14481 and maybe a little bit to https://github.com/scikit-learn/scikit-learn/issues/13986.

My understanding of the `copy=False` parameter of estimators is "allow inplace modifications of X".

When avoiding a copy is not possible (X doesn't have the right dtype or memory layout for instance), a copy is still triggered. I believe that X being read-only is a valid reason for still triggering a copy.

My main argument is that the user isn't always in control of the permissions of an input array within the whole pipeline. Especially when joblib parallelism is enabled, which may create read-only memmaps. We've have a bunch of issues because of that, the latest being https://github.com/scikit-learn/scikit-learn/issues/28781. And it's poorly tested because it requires big arrays which we try to avoid in the tests (although joblib 1.13 makes it easy to trigger with small arrays).

I wouldn't make `check_array(copy=False)` always trigger a copy when X is read-only because the semantic of the `copy` param of `check_array` is not the same as the one of estimators. We could introduce a new param in check_array, like `copy_if_readonly` ?
- Estimator has no copy param (i.e.) doesn't intend to do inplace modification:
  `check_array(copy=False, copy_if_readonly=False)`
- Estimator has copy param:
  `check_array(copy=self.copy, copy_if_readonly=True)`

It could also be a third option for copy in check_array: True, False, "if_readonly":
- Estimator has no copy param:
  `check_array(copy=False)`
- Estimator has copy param:
  `check_array(copy=self.copy or "if_readonly")`

COMMENT:
>  My main argument is that the user isn't always in control of the permissions of an input array within the whole pipeline. Especially when joblib parallelism is enabled, which may create read-only memmaps.

> It seems useful to not raise an error in these cases.

> After thinking about this a bit more: what is the problem we are trying to solve?

This is exactly the main problem I want to solve: Stop having to worry that a read-only array being created at some point can cause an error. To me it should be covered by the fact that `copy=False` means don't copy unless required.

One way of ending up with a read-only array is with joblib's memmapping. It caused many issues, the last one being https://github.com/scikit-learn/scikit-learn/issues/28781.

Thinking more about it, maybe there are 2 things to consider here:
- How `check_array` deals with read-only memmaps

  -> We need to make a copy because we can't make a read-only memmap writeable.

- How `check_array` deals with other read-only array-likes

  -> We don't need to make a copy, we just need to make it writeable.
  Here the question would be: if a user passes a read-only array and copy=False to an estimator, should we make it writeable or raise an error ?

In this regard, I think a new parameter is more appropriate. I would propose `writeable=True/None`:
- None is the default and means leave the output array as is, no guarantee about it being writeable or not.
- True means
    - if copy=True, just make sure the copy is writeable
    - if copy=False:
        - if a copy must be made to make it writeable (e.g. read-only memmap), make a copy
        - if it can be made writeable without copy, I'm not sure here between doing it, make a copy or raise an error
- There's no False option. I don't see a single situation where we need to turn an array read-only. I don't see writeable as a boolean but more as a toggle.

Labels: RFC