diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomMarshallerAttributeAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomMarshallerAttributeAnalyzer.cs index f5ed61fb817e5d..b7a49f6d33ac5d 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomMarshallerAttributeAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/CustomMarshallerAttributeAnalyzer.cs @@ -882,7 +882,7 @@ void ReportDiagnosticsForMismatchedMemberSignatures(DiagnosticReporter diagnosti // First verify all usages in the managed->unmanaged shape. IMethodSymbol toUnmanagedMethod = methods.ToUnmanaged ?? methods.ToUnmanagedWithBuffer; unmanagedType = toUnmanagedMethod.ReturnType; - if (!unmanagedType.IsUnmanagedType && !unmanagedType.IsStrictlyBlittable()) + if (!unmanagedType.IsUnmanagedType && !unmanagedType.IsStrictlyBlittableInContext(_compilation)) { diagnosticReporter.CreateAndReportDiagnostic(UnmanagedTypeMustBeUnmanagedRule, toUnmanagedMethod.ToDisplayString()); } @@ -1198,7 +1198,7 @@ void ReportDiagnosticsForMismatchedMemberSignatures(DiagnosticReporter diagnosti { // First verify all usages in the managed->unmanaged shape. unmanagedType = methods.ToUnmanaged.ReturnType; - if (!unmanagedType.IsUnmanagedType && !unmanagedType.IsStrictlyBlittable()) + if (!unmanagedType.IsUnmanagedType && !unmanagedType.IsStrictlyBlittableInContext(_compilation)) { diagnosticReporter.CreateAndReportDiagnostic(UnmanagedTypeMustBeUnmanagedRule, methods.ToUnmanaged.ToDisplayString()); } @@ -1217,7 +1217,7 @@ void ReportDiagnosticsForMismatchedMemberSignatures(DiagnosticReporter diagnosti { unmanagedType = fromUnmanagedMethod.Parameters[0].Type; - if (!unmanagedType.IsUnmanagedType && !unmanagedType.IsStrictlyBlittable()) + if (!unmanagedType.IsUnmanagedType && !unmanagedType.IsStrictlyBlittableInContext(_compilation)) { diagnosticReporter.CreateAndReportDiagnostic(UnmanagedTypeMustBeUnmanagedRule, fromUnmanagedMethod.ToDisplayString()); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BlittableTypeMarshallingInfoProvider.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BlittableTypeMarshallingInfoProvider.cs index cc3a1996957f19..04b2da38da3265 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BlittableTypeMarshallingInfoProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/BlittableTypeMarshallingInfoProvider.cs @@ -38,7 +38,7 @@ public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth } else { - return new UnmanagedBlittableMarshallingInfo(type.IsStrictlyBlittable()); + return new UnmanagedBlittableMarshallingInfo(type.IsStrictlyBlittableInContext(_compilation)); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs index 439c2b3e46d106..2f1e4603440e6b 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/ManualTypeMarshallingHelper.cs @@ -526,7 +526,7 @@ or MarshalMode.UnmanagedToManagedRef ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), HasState: false, shape, - nativeType.IsStrictlyBlittable(), + nativeType.IsStrictlyBlittableInContext(compilation), bufferElementType, collectionElementTypeInfo, collectionElementMarshallingInfo); @@ -606,7 +606,7 @@ or MarshalMode.UnmanagedToManagedRef ManagedTypeInfo.CreateTypeInfoForTypeSymbol(nativeType), HasState: true, shape, - nativeType.IsStrictlyBlittable(), + nativeType.IsStrictlyBlittableInContext(compilation), bufferElementType, CollectionElementType: collectionElementTypeInfo, CollectionElementMarshallingInfo: collectionElementMarshallingInfo); diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs index 1affbba906e4bd..25a695a446c133 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypeSymbolExtensions.cs @@ -30,10 +30,11 @@ public static bool IsConsideredBlittable(this ITypeSymbol type) { unsafe { - return IsBlittableWorker(type, ImmutableHashSet.Create(SymbolEqualityComparer.Default), &IsConsideredBlittableWorker); + // We can pass a null Compilation here since our blittability check does not depend on the compilation. + return IsBlittableWorker(type, ImmutableHashSet.Create(SymbolEqualityComparer.Default), compilation: null!, &IsConsideredBlittableWorker); } - static bool IsConsideredBlittableWorker(ITypeSymbol t, ImmutableHashSet seenTypes) + static bool IsConsideredBlittableWorker(ITypeSymbol t, ImmutableHashSet seenTypes, Compilation compilation) { return t.IsUnmanagedType; } @@ -50,14 +51,15 @@ static bool IsConsideredBlittableWorker(ITypeSymbol t, ImmutableHashSet /// The type to check. /// Returns true if strictly blittable, otherwise false. - public static bool IsStrictlyBlittable(this ITypeSymbol type) + /// The compilation context of the source being compiled. + public static bool IsStrictlyBlittableInContext(this ITypeSymbol type, Compilation compilation) { unsafe { - return IsBlittableWorker(type, ImmutableHashSet.Create(SymbolEqualityComparer.Default), &IsStrictlyBlittableWorker); + return IsBlittableWorker(type, ImmutableHashSet.Create(SymbolEqualityComparer.Default), compilation, &IsStrictlyBlittableWorker); } - static unsafe bool IsStrictlyBlittableWorker(ITypeSymbol t, ImmutableHashSet seenTypes) + static unsafe bool IsStrictlyBlittableWorker(ITypeSymbol t, ImmutableHashSet seenTypes, Compilation compilation) { if (t.SpecialType is not SpecialType.None) { @@ -65,23 +67,22 @@ static unsafe bool IsStrictlyBlittableWorker(ITypeSymbol t, ImmutableHashSet seenTypes, delegate*, bool> isBlittable) + private static unsafe bool IsBlittableWorker(this ITypeSymbol type, ImmutableHashSet seenTypes, Compilation compilation, delegate*, Compilation, bool> isBlittable) { // Assume that type parameters that can be blittable are blittable. // We'll re-evaluate blittability for generic fields of generic types at instantiation time. @@ -89,7 +90,14 @@ private static unsafe bool IsBlittableWorker(this ITypeSymbol type, ImmutableHas { return true; } - if (type.IsAutoLayout() || !isBlittable(type, seenTypes)) + + // Treat pointers as always blittable. + if (type.TypeKind is TypeKind.Pointer or TypeKind.FunctionPointer) + { + return true; + } + + if (type.IsAutoLayout() || !isBlittable(type, seenTypes, compilation)) { return false; } @@ -121,7 +129,7 @@ private static bool IsAutoLayout(this ITypeSymbol type) return type.IsReferenceType; } - private static unsafe bool HasOnlyBlittableFields(this ITypeSymbol type, ImmutableHashSet seenTypes, delegate*, bool> isBlittable) + private static unsafe bool HasOnlyBlittableFields(this ITypeSymbol type, ImmutableHashSet seenTypes, Compilation compilation, delegate*, Compilation, bool> isBlittable) { if (seenTypes.Contains(type)) { @@ -134,7 +142,7 @@ private static unsafe bool HasOnlyBlittableFields(this ITypeSymbol type, Immutab { if (!field.IsStatic) { - if (!IsBlittableWorker(field.Type, seenTypes.Add(type), isBlittable)) + if (!IsBlittableWorker(field.Type, seenTypes.Add(type), compilation, isBlittable)) { return false; } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index 0f0cb60da38783..ca5c587da7442d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -882,35 +882,43 @@ public async Task ValidateSnippets_InvalidCodeGracefulFailure(string id, string [Fact] public async Task ValidateDisableRuntimeMarshallingForBlittabilityCheckFromAssemblyReference() { - // Emit the referenced assembly to a stream so we reference it through a metadata reference. - // Our check for strict blittability doesn't work correctly when using source compilation references. - // (There are sometimes false-positives.) - // This causes any diagnostics that depend on strict blittability being correctly calculated to - // not show up in the IDE experience. However, since they correctly show up when doing builds, - // either by running the Build command in the IDE or a command line build, we aren't allowing invalid code. - // This test validates the Build-like experience. In the future, we should update this test to validate the - // IDE-like experience once we fix that case - // (If the IDE experience works, then the command-line experience will also work.) - // This bug is tracked in https://github.com/dotnet/runtime/issues/84739. string assemblySource = $$""" using System.Runtime.InteropServices.Marshalling; {{CodeSnippets.ValidateDisableRuntimeMarshalling.NonBlittableUserDefinedTypeWithNativeType}} """; - Compilation assemblyComp = await TestUtils.CreateCompilation(assemblySource); - Assert.Empty(assemblyComp.GetDiagnostics()); - - var ms = new MemoryStream(); - Assert.True(assemblyComp.Emit(ms).Success); string testSource = CodeSnippets.ValidateDisableRuntimeMarshalling.TypeUsage(string.Empty); + const string AdditionalProjectName = "AdditionalProject"; + VerifyCS.Test test = new(referenceAncillaryInterop: false) { - TestCode = testSource, + TestState = + { + Sources = + { + testSource + }, + AdditionalProjectReferences = + { + AdditionalProjectName + }, + AdditionalProjects = + { + [AdditionalProjectName] = + { + Sources = + { + assemblySource + + } + } + } + }, TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck }; - test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromImage(ms.ToArray())); + test.TestState.AdditionalProjects[AdditionalProjectName].AdditionalReferences.AddRange(test.TestState.AdditionalReferences); // The errors should indicate the DisableRuntimeMarshalling is required. test.ExpectedDiagnostics.Add(