-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Add S.R.CS.Unsafe.AsRef<T>(ref readonly T) #24479
Conversation
|
||
#ifdef netcoreapp | ||
#else | ||
.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@VSadov I see that Roslyn is marking the type with Microsoft.CodeAnalysis.EmbeddedAttribute
. Is it ok to skip it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you don't need to mark as embedded (since this type was not embedded by the compiler).
In reply to: 143267591 [](ancestors = 143267591)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @OmarTawfik already added the IsReadOnlyAttribute
type to coreCLR and coreRT repos. Can you reference it instead of defining it again? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This package targets older runtimes too. The local definition is needed for targeting of the older runtimes. ifdef netcoreapp
avoids the local definition for .NET Core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is also added to desktop 4.7.1. Not sure if this infrastructure takes that into account:
https://apisof.net/catalog/System.Runtime.CompilerServices.IsReadOnlyAttribute
int[] a = new int[] { 0x123, 0x234, 0x345, 0x456 }; | ||
|
||
ref int r = ref Unsafe.AsRef<int>(a[0]); | ||
Assert.Equal(0x123, r); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It maybe also useful to try to assign to r
and verify that the value shows up in a[0]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that only guaranteed if the in
keyword is specified at the call-site?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The in
syntax for readonly refs was removed (dotnet/roslyn#22182) because of it was redundant. I do not think it is coming back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was changed to be in
rather than ref readonly
approx 10 days after 22182: dotnet/roslyn#22387
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
‘in’ provides guarantees of aliasing. No modifier at callsite just makes the best effort. In simple cases there is no difference. Complex cases are not interesting here since we are not testing the compiler.
It would be interesting though to get a ref to something readonly - like a readonly field, assign a new value, and observe the changed value by directly reading the field (not via AsRef).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ektrah Do you plan to make this addition to the test? E.g. something like:
r = 0x42;
Assert.Equal(0x42, a[0]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkotas Given that "No modifier at callsite just makes the best effort." it seems that such a test could randomly fail with the current compiler version. So I would keep the test as it is for now. I'm happy to provide an update later when a syntax to guarantee aliasing is available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It cannot fail randomly. The compiler has to be deterministic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior is not random :). Lvalues of compatible type will not be passed via copy.
It would be a good practice to use “in” at call site for methods like this though - since accidentally getting in situation when copy is made would be undesirable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'm convinced. I've added the above check to the test.
@@ -281,6 +281,20 @@ | |||
#endif | |||
} // end of method Unsafe::AsRef | |||
|
|||
.method public hidebysig static !!T& AsRef<T>(!!T& source) cil managed aggressiveinlining |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this supposed to also have a modreq
: https://github.com/dotnet/csharplang/blob/ac995bd794be5a17acb52c30dfa4d61a1bfd15c9/proposals/csharp-7.2/readonly-ref.md#metadata-representation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modregs are used on return ref readonly
only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't that make it impossible to have both ref and ref read-only overloads, since the would compile down to the same il signature? As far as I know, the latest decision was to allow both ovetloads and only require the in
keyword if the decision was ambiguous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to make this change work it has to match what the current compiler does. If there are plans to keep tweaking the implementation, this will need to be updated to match. It is common for compiler upgrade in corefx to require accompanying source changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the current bits/tests in master, a modreq is required: https://github.com/dotnet/roslyn/blob/1fd9a04c8c0d13afdd7af546fa7dce83549d6aba/src/Compilers/CSharp/Test/Emit/Emit/InAttributeModifierTests.cs#L205 (others as well, if you just search for modreq).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think modreq is required in this case. It is not a return or a parameter of something overridable.
Try compiling similar signature in c# just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compiling the signature in C# doesn't seem to generate a modreq; see #24479 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@VSadov, I would fully expect the modreq to be required, otherwise you cannot disambiguate (from the IL method signatures alone) whether it is ref
or ref readonly
(and therefore cannot overload between the two).
The C# signature, as it is currently defined, would be: public static ref T AsRef<T>([IsReadOnly] ref T source)
. Given that the attribute is not part of the IL signature, it is not-distinguishable from public static ref T AsRef<T>(ref T source)
(both compile to an IL signature of .method public hidebysig static !!T& AsRef<T>(!!T& source) cil managed aggressiveinlining
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@VSadov, I checked the C# behavior in current master and decided to send a mail.
I think there are some clear issues with what we are currently doing and some not so clear (or just plain missing) documents on what the final expected behavior is.
From a basic standpoint:
- Converting an existing method from
ref
toin
should not be breaking (it currently is)- This only applies if you can't have both
ref
andin
overloads (as it is currently)
- This only applies if you can't have both
- The marshaller should not need to be updated in order to work with
in
parameters- The compiler should be emitting the
[In]
attribute, as it does the[Out]
attribute forout
parameters
- The compiler should be emitting the
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tannergooding I believe there is some confusion here. The modreq is not used to distinguish between in
parameters and other ref kinds. The attribute IsReadOnly
is used for that purpose (and emitted in all cases). The modreq is only used in cases where we want to prevent older compilers/other languages to compile implementations/overrides without understanding the semantics. Am I missing something?
@dotnet-bot test this please |
For reference: namespace System.Runtime.CompilerServices
{
public static class Unsafe
{
public static ref T AsRef<T>(ref readonly T source)
{
throw null;
}
}
} in a .NET Standard 2.0 library project is compiled by Microsoft.NETCore.Compilers 2.6.0-beta1-62126-01 to
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, modulo test nit.
@@ -16,6 +16,7 @@ public static partial class Unsafe | |||
public static bool AreSame<T>(ref T left, ref T right) { throw null; } | |||
public unsafe static void* AsPointer<T>(ref T value) { throw null; } | |||
public unsafe static ref T AsRef<T>(void* source) { throw null; } | |||
public static ref T AsRef<T>(ref readonly T source) { throw null; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: when you next update your compiler toolset, this will have to be changed to use in
instead of ref readonly
. I don't see the LDM notes on this posted yet, but here's a summary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thank you! |
Commit migrated from dotnet/corefx@7223105
Resolves #23916