Skip to content

Support specific members from System.[U]IntPtr#41728

Merged
cston merged 7 commits intodotnet:features/NativeIntfrom
cston:members
Feb 26, 2020
Merged

Support specific members from System.[U]IntPtr#41728
cston merged 7 commits intodotnet:features/NativeIntfrom
cston:members

Conversation

@cston
Copy link
Contributor

@cston cston commented Feb 15, 2020

Expose overridden methods GetHashCode(), Equals(object obj), and ToString() from nint and nuint.
Test plan #38821

@cston cston requested a review from a team as a code owner February 15, 2020 22:07
@cston
Copy link
Contributor Author

cston commented Feb 20, 2020

@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.
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// 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

Copy link
Contributor Author

@cston cston Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove the members from NativeIntegerTypeSymbol for now.


In reply to: 382719607 [](ancestors = 382719607,382316178,382302890,382292681)

}
}

public sealed override bool ReturnsVoid
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public sealed override bool ReturnsVoid [](start = 8, length = 39)

Is this covered by WrappedMethodSymbol? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.


In reply to: 382294420 [](ancestors = 382294420)

internal string lazyDefaultMemberName;
internal NamedTypeSymbol lazyComImportCoClassType = ErrorTypeSymbol.UnknownResultType;
internal ThreeState lazyHasEmbeddedAttribute = ThreeState.Unknown;
internal NativeIntegerTypeSymbol lazyNativeInt;
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See MetadataOrSourceAssemblySymbol._lazySpecialTypes


In reply to: 382297303 [](ancestors = 382297303)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for types, for example typeof(nint).


In reply to: 382302745 [](ancestors = 382302745)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NativeIntegerTypeSymbol [](start = 26, length = 23)

It feels like we need to be able to retarget these types. Do we handle that? #Pending

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a PROTOTYPE comment for now.


In reply to: 382305226 [](ancestors = 382305226)

private static void VerifyMembers(NamedTypeSymbol type)
{
var memberNames = type.MemberNames;
var allMembers = type.GetMembers();
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 20, 2020

Done with review pass (iteration 2) #Closed

}
}

private sealed class NativeIntegerMethodSymbol : WrappedMethodSymbol
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NativeIntegerParameterSymbol [](start = 29, length = 28)

Same question about equality. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Feb 24, 2020

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.
Copy link
Contributor

@AlekseyTs AlekseyTs Feb 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@cston
Copy link
Contributor Author

cston commented Feb 24, 2020

@dotnet/roslyn-compiler, please review. The change is now limited to caching symbols on the containing assembly.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (iteration 7). Please make sure to adjust final commit message to reflect the actual changes in the PR.

Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@cston cston merged commit 3ec85a1 into dotnet:features/NativeInt Feb 26, 2020
@cston cston deleted the members branch February 26, 2020 20:46
cston added a commit to cston/roslyn that referenced this pull request Apr 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants