- "Not all Unsafe is created equal"
This C# change is mostly a reaction to a change in the BCL's thinking around IntPtr
, and how generic math will apply to it. During the design of
nint
, we wanted to keep nint
and IntPtr
as distinct as possible, as the intent was not to enable general math on IntPtr
s. However, as the
BCL has rationalized their generic math designs, they've decided that the easiest way forward will be to remove this distinction, and enable
generic math on IntPtr
. We therefore want to update C# to similarly remove the distinction, and regard nint
as simple alias for IntPtr
(and
nuint
a simple alias for UIntPtr
) to keep the language and runtime in sync.
Accepted.
We wanted to revisit the decision we made last time, as further discussion after the meeting
revealed that we had conflated a few features in the decision, and we wanted to break them out and be sure that we are comfortable with feature.
In particular, we did not explore the analyzer route enough, as further investigation revealed that, after the in
/ref
mismatch was dealt with,
nothing prevented using an analyzer to prevent rvalues being passed to in
parameters. There are 3 main components, therefore, that this proposal
seeks to address:
- There is no way to move an existing API from
ref
toin
without a source-breaking change. We addressed this issue by allowingref
to be used forin
parameters, and did not discuss changing this today. - At the declaration site, there is no way to indicate that being passed an rvalue from the call site is likely an error. This was our main discussion today.
- At the call site, lack of required
in
makes it unclear that a ref is being captured. This was not covered today, and we'll try to squeeze a discussion in next meeting.
On the surface, points 2 and 3 appear to be a good fit for an analyzer, but we think there might an opportunity to address a lingering annoyance
from the original design of in
parameters: the in
keyword does not correspond to ref readonly
, the keyword we use everywhere else throughout
the language for a similar concept. We see an opportunity to retcon in
to be a modifier with extra behavior on top of ref readonly
, much like
out
is a modifier on top of ref
with extra behavior on top. There is also the potential for an analyzer here to make the language more complex,
not less, as now this spot in the table of declaration site modifiers/permitted call site forms would be taken by an analyzer, not by a native
language feature.
The history of in
as a feature stretches back to the time some members of the LDT spent on the Midori project, where they had a custom version of
C# that supported a number of features, including a version of ref readonly
. Initially, they used ref readonly
as both the declaration site and
the call site modifier, but user feedback, particularly on the call site side, was immediate and vocal. To combat this, they brainstormed and
settled on in
(first just at the call site, then eventually as a parameter modifier), as it's the natural counterpart to out
and has been used
as a C# keyword since C# 1.0. This was then brought into C# in the C# 7 timeframe, as we added ref struct
s based on Midori's experience in the
area. Midori, however, didn't have as many of these APIs where passing an rvalue was incorrect, as it had an expanded permissions system for
lifetime control. C# does not have this, so we think that ref readonly
is a good change to make up for this.
Next, we will look at how ref readonly
should affect the call site, and whether it should require that ref
, in
, or some other specifier to
be explicitly provided.
We upload the decision around ref readonly
as a parameter modifier.