Skip to content

Conversation

@jkoritzinsky
Copy link
Member

Generate the partial function as a DllImport when a full-body generated stub would be a no-op wrapper around an inner DllImport.

Fixes #60596

…ed stub would be a no-op wrapper around an inner DllImport.
…c for blittable scenarios as well since we now turn a GeneratedDllImport into a DllImport when the stub would do nothing,.
@jkoritzinsky jkoritzinsky added area-System.Runtime.InteropServices source-generator Indicates an issue with a source generator feature labels Oct 19, 2021
@jkoritzinsky jkoritzinsky added this to the 7.0.0 milestone Oct 19, 2021
Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Choose a reason for hiding this comment

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

This is great. I need this for the "unsupported framework" fallback. I have it all set up except the forwarder to DllImport logic.

@am11
Copy link
Member

am11 commented Oct 19, 2021

Crossgen2 considers Boolean and Char as billitable types:

[Theory]
[InlineData(WellKnownType.Void)]
[InlineData(WellKnownType.Boolean)]
[InlineData(WellKnownType.Char)]
[InlineData(WellKnownType.SByte)]
[InlineData(WellKnownType.Byte)]
[InlineData(WellKnownType.Int16)]
[InlineData(WellKnownType.UInt16)]
[InlineData(WellKnownType.Int32)]
[InlineData(WellKnownType.UInt32)]
[InlineData(WellKnownType.Int64)]
[InlineData(WellKnownType.UInt64)]
[InlineData(WellKnownType.IntPtr)]
[InlineData(WellKnownType.UIntPtr)]
[InlineData(WellKnownType.Single)]
[InlineData(WellKnownType.Double)]
[InlineData(WellKnownType.RuntimeFieldHandle)]
[InlineData(WellKnownType.RuntimeTypeHandle)]
[InlineData(WellKnownType.RuntimeMethodHandle)]
public void IsBlittableType_BilittableWellKnownTypes_ReturnsTrue(WellKnownType type) =>
Assert.True(MarshalUtils.IsBlittableType(_context.GetWellKnownType(type)));
[Theory]
[InlineData(WellKnownType.String)]
[InlineData(WellKnownType.ValueType)]
[InlineData(WellKnownType.Enum)]
[InlineData(WellKnownType.Array)]
[InlineData(WellKnownType.MulticastDelegate)]
[InlineData(WellKnownType.Exception)]
[InlineData(WellKnownType.Object)]
public void IsBlittableType_NonBilittableWellKnownTypes_ReturnsFalse(WellKnownType type) =>
Assert.False(MarshalUtils.IsBlittableType(_context.GetWellKnownType(type)));
but TypeSymbolExtensions doesn't:
private static bool IsSpecialTypeBlittable(SpecialType specialType)
=> specialType switch
{
SpecialType.System_Void
or SpecialType.System_SByte
or SpecialType.System_Byte
or SpecialType.System_Int16
or SpecialType.System_UInt16
or SpecialType.System_Int32
or SpecialType.System_UInt32
or SpecialType.System_Int64
or SpecialType.System_UInt64
or SpecialType.System_Single
or SpecialType.System_Double
or SpecialType.System_IntPtr
or SpecialType.System_UIntPtr => true,

should they match?

@jkoritzinsky
Copy link
Member Author

In this case, crossgen2 is incorrect. Bool and Char are not blittable in the current system (which is the whole reason blittable != unmanaged)

@am11
Copy link
Member

am11 commented Oct 19, 2021

Ah right. The test was added as part of addressing #46653 (comment), which suggests crossgen2 case is different (crossgen2 is not involved in Marshal.SizeOf).

@am11
Copy link
Member

am11 commented Oct 19, 2021

Perhaps we can split IsBlittable and RequiresSpecialMarshalling methods in crossgen2 to avoid confusion. cc @MichalStrehovsky.

@MichalStrehovsky
Copy link
Member

Crossgen2 considers Boolean and Char as billitable types:

It doesn't. The unit test just uses a weird base class library where Bool and Char have no fields:

public struct Boolean { }
public struct Char { }

If you zoom into IsBlittableType implementation:

public static bool IsBlittableType(TypeDesc type)
{
if (!type.IsDefType)
{
return false;
}
TypeDesc baseType = type.BaseType;
bool hasNonTrivialParent = baseType != null
&& !baseType.IsWellKnownType(WellKnownType.Object)
&& !baseType.IsWellKnownType(WellKnownType.ValueType);
if (hasNonTrivialParent && !IsBlittableType(baseType))
{
return false;
}
var mdType = (MetadataType)type;
if (!mdType.IsSequentialLayout && !mdType.IsExplicitLayout)
{
return false;
}
foreach (FieldDesc field in type.GetFields())
{
if (field.IsStatic)
{
continue;
}
MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(
field.FieldType,
parameterIndex : null,
customModifierData: null,
field.GetMarshalAsDescriptor(),
isReturn: false,
isAnsi: mdType.PInvokeStringFormat == PInvokeStringFormat.AnsiClass,
MarshallerType.Field,
elementMarshallerKind: out var _);
if (marshallerKind != MarshallerKind.Enum
&& marshallerKind != MarshallerKind.BlittableValue
&& marshallerKind != MarshallerKind.BlittableStruct
&& marshallerKind != MarshallerKind.UnicodeChar)
{
return false;
}
}
return true;
}

You can see why we would end up saying the struct is blittable if there's no fields.

Maybe the code in MarshalUtils should check for primitive types first, but as things are now I think it kind of works for real core libraries. I think we would see bugs if it didn't work.

@jkoritzinsky
Copy link
Member Author

We definitely would definitely see bugs if this was broken in real scenarios. I was very confused about how it was broken.

…nerator.UnitTests/Compiles.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@jkoritzinsky jkoritzinsky merged commit ee4a76d into dotnet:main Oct 20, 2021
@jkoritzinsky jkoritzinsky deleted the dllimportgen-blittable-in-place branch October 20, 2021 15:51
@ghost ghost locked as resolved and limited conversation to collaborators Nov 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtime.InteropServices source-generator Indicates an issue with a source generator feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DllImportGenerator] Improve handling of blittable signatures in source generator

6 participants