Support specific members from System.[U]IntPtr#41728
Support specific members from System.[U]IntPtr#41728cston merged 7 commits intodotnet:features/NativeIntfrom
Conversation
|
@dotnet/roslyn-compiler please review, thanks. |
| /// sizeof(nint) should be used instead of Size; | ||
| /// + and - should be used instead of Add() and Subtract(); | ||
| /// ToInt32(), ToInt64(), ToPointer() should be used from System.IntPtr only; | ||
| /// overridden methods Equals(), GetHashCode(), and ToString() are referenced from System.Object. |
There was a problem hiding this comment.
/// overridden methods Equals(), GetHashCode(), and ToString() are referenced from System.Object. [](start = 8, length = 97)
Just curious, why doing this wasn't good enough? #Closed
There was a problem hiding this comment.
It resulted in bad code generated for callers such as F() below:
class C
{
readonly nint n;
string F() => n.ToString();
}
Result:
IL_0000: ldarg.0
IL_0001: ldflda native int C::n
IL_0006: constrained. [mscorlib]System.IntPtr
IL_000c: callvirt instance string [mscorlib]System.Object::ToString()
IL_0011: ret
Expected:
.locals init (native int V_0)
IL_0000: ldarg.0
IL_0001: ldfld native int C::n
IL_0006: stloc.0
IL_0007: ldloca.s V_0
IL_0009: call instance string [mscorlib]System.IntPtr::ToString()
IL_000e: ret
See ReadOnlyField_VirtualMethods test.
In reply to: 382292681 [](ancestors = 382292681)
There was a problem hiding this comment.
It resulted in bad code generated for callers
Let's imaging there is a virtual method in base that IntPtr doesn't override. Would we generate the same bad code for that method invocation?
In reply to: 382302890 [](ancestors = 382302890,382292681)
There was a problem hiding this comment.
It sounds like there is a bug in the verifier. I can build and successfully run this code:
class C
{
readonly nint n1;
readonly System.IntPtr n2;
string F1() => n1.ToString();
string F2() => n2.ToString();
static void Main()
{
System.Console.WriteLine(new C().F1());
System.Console.WriteLine(new C().F2());
}
}
struct nint
{}
But PEVerify complains about IL in F1.
So, if that verification error is the only reason to synthesize members, I think we should give this another thought.
In reply to: 382316178 [](ancestors = 382316178,382302890,382292681)
There was a problem hiding this comment.
I'll remove the members from NativeIntegerTypeSymbol for now.
In reply to: 382719607 [](ancestors = 382719607,382316178,382302890,382292681)
| } | ||
| } | ||
|
|
||
| public sealed override bool ReturnsVoid |
There was a problem hiding this comment.
public sealed override bool ReturnsVoid [](start = 8, length = 39)
Is this covered by WrappedMethodSymbol? #Closed
There was a problem hiding this comment.
| internal string lazyDefaultMemberName; | ||
| internal NamedTypeSymbol lazyComImportCoClassType = ErrorTypeSymbol.UnknownResultType; | ||
| internal ThreeState lazyHasEmbeddedAttribute = ThreeState.Unknown; | ||
| internal NativeIntegerTypeSymbol lazyNativeInt; |
There was a problem hiding this comment.
internal NativeIntegerTypeSymbol lazyNativeInt; [](start = 12, length = 47)
I think we can cache this on the assembly symbol. It feels wasteful to spend this memory for every "uncommon" type symbol just so we could use this slot on two types. #Closed
There was a problem hiding this comment.
See MetadataOrSourceAssemblySymbol._lazySpecialTypes
In reply to: 382297303 [](ancestors = 382297303)
There was a problem hiding this comment.
We can simply add two more slots at the end of the array
In reply to: 382299283 [](ancestors = 382299283,382297303)
|
|
||
| private ThreeState _lazyIsExplicitDefinitionOfNoPiaLocalType = ThreeState.Unknown; | ||
|
|
||
| private NativeIntegerTypeSymbol _lazyNativeInt; |
There was a problem hiding this comment.
private NativeIntegerTypeSymbol _lazyNativeInt; [](start = 8, length = 47)
Same comment as for PE #Closed
| /// There are no members on <see cref="System.IntPtr"/> that should be exposed directly on nint: | ||
| /// There only members of <see cref="System.IntPtr"/> that are exposed directly on nint | ||
| /// are the overridden methods Equals(), GetHashCode(), and ToString(). For the other members: | ||
| /// constructors for nint other than the default parameterless constructor are not supported; |
There was a problem hiding this comment.
constructors for nint other than the default parameterless constructor are not supported; [](start = 12, length = 89)
Should we add synthesized parameter-less constructors then? I believe we do this for all structs, even imported ones. #Closed
| accessWithinOpt: null); | ||
| if (method is object) | ||
| { | ||
| builder.Add(new NativeIntegerMethodSymbol(containingType, method)); |
There was a problem hiding this comment.
new NativeIntegerMethodSymbol(containingType, method) [](start = 32, length = 53)
Where do we make sure the methods are unified with underlying methods for the purpose of IL generation? #Closed
There was a problem hiding this comment.
There was a problem hiding this comment.
I think it would be good to find a good place to assert that Cci layer never sees instances of these synthetic symbols, both types and members.
In reply to: 382303254 [](ancestors = 382303254,382302745)
|
|
||
| namespace Microsoft.CodeAnalysis.CSharp.Symbols | ||
| { | ||
| internal sealed class NativeIntegerTypeSymbol : WrappedNamedTypeSymbol |
There was a problem hiding this comment.
NativeIntegerTypeSymbol [](start = 26, length = 23)
It feels like we need to be able to retarget these types. Do we handle that? #Pending
There was a problem hiding this comment.
| private static void VerifyMembers(NamedTypeSymbol type) | ||
| { | ||
| var memberNames = type.MemberNames; | ||
| var allMembers = type.GetMembers(); |
There was a problem hiding this comment.
allMembers [](start = 16, length = 10)
I think it would be good to assert that, each time we call GetMembers, we get the same instances for members. #Closed
| /// <summary> | ||
| /// There are no members on <see cref="System.IntPtr"/> that should be exposed directly on nint: | ||
| /// There only members of <see cref="System.IntPtr"/> that are exposed directly on nint | ||
| /// are the overridden methods Equals(), GetHashCode(), and ToString(). For the other members: |
There was a problem hiding this comment.
are the overridden methods Equals(), GetHashCode(), and ToString() [](start = 12, length = 66)
Should we instead build the set dynamically by picking up all members that are overriding members from underlying? #Closed
| // CodeGenerator.EmitCallExpression() is not expecting a System.ValueType without an overload of ToString(). | ||
| [Fact(Skip = "PEVerify failure")] | ||
| [Fact] | ||
| public void ReadOnlyField_VirtualMethods() |
There was a problem hiding this comment.
ReadOnlyField_VirtualMethods [](start = 20, length = 28)
I think it would be good to also test delegate creations with these methods. Also, expression trees with calls and delegate creations. #Closed
|
Done with review pass (iteration 2) #Closed |
| } | ||
| } | ||
|
|
||
| private sealed class NativeIntegerMethodSymbol : WrappedMethodSymbol |
There was a problem hiding this comment.
NativeIntegerMethodSymbol [](start = 29, length = 25)
It looks like container is equal to underlying, but members are not. Should they be? #Closed
| internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) => throw ExceptionUtilities.Unreachable; | ||
| } | ||
|
|
||
| private sealed class NativeIntegerParameterSymbol : WrappedParameterSymbol |
There was a problem hiding this comment.
NativeIntegerParameterSymbol [](start = 29, length = 28)
Same question about equality. #Closed
|
Done with review pass (iteration 3) #Closed |
| {42.GetHashCode()}"); | ||
| // PROTOTYPE: Should we generate the following instead, which would correspond to the equivalent | ||
| // code generated for `_i.ToString()` if `_i` was `int`? If NativeIntegerTypeSymbol.GetMembers() included | ||
| // members for ToString(), Equals(), GetHashCode(), that would fix this issue and the PEVerify failure above. |
There was a problem hiding this comment.
members for ToString(), Equals(), GetHashCode(), that would fix this issue and the PEVerify failure above. [](start = 15, length = 106)
Consider explicitly stating that the code we generate right now is equivalent to a code for a regular structure that doesn't override ToString() #Closed
|
@dotnet/roslyn-compiler, please review. The change is now limited to caching symbols on the containing assembly. |
AlekseyTs
left a comment
There was a problem hiding this comment.
LGTM (iteration 7). Please make sure to adjust final commit message to reflect the actual changes in the PR.
Expose overridden methods
GetHashCode(),Equals(object obj), andToString()fromnintandnuint.Test plan #38821