diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs index 07147b1110323..31cd4455663e2 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -29,6 +29,11 @@ public static IEnumerable Generics () return TestNamesBySuiteName (); } + public static IEnumerable InlineArrays () + { + return TestNamesBySuiteName(); + } + public static IEnumerable LinkXml() { return TestNamesBySuiteName(); diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs index 883582cc5d9df..744fb23e416f9 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -30,6 +30,13 @@ public void Generics (string t) Run (t); } + [Theory] + [MemberData(nameof(TestDatabase.InlineArrays), MemberType = typeof(TestDatabase))] + public void InlineArrays(string t) + { + Run(t); + } + [Theory] [MemberData (nameof (TestDatabase.LinkXml), MemberType = typeof (TestDatabase))] public void LinkXml (string t) diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs index e97a51a5c2c73..6441776f33ad0 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs @@ -129,9 +129,11 @@ public virtual IEnumerable GetCommonReferencedAssemblies (NPath workingD yield return Path.Combine (referenceDir, "mscorlib.dll"); yield return Path.Combine (referenceDir, "System.Collections.dll"); + yield return Path.Combine (referenceDir, "System.Collections.Immutable.dll"); yield return Path.Combine (referenceDir, "System.ComponentModel.TypeConverter.dll"); yield return Path.Combine (referenceDir, "System.Console.dll"); yield return Path.Combine (referenceDir, "System.Linq.Expressions.dll"); + yield return Path.Combine (referenceDir, "System.Memory.dll"); yield return Path.Combine (referenceDir, "System.ObjectModel.dll"); yield return Path.Combine (referenceDir, "System.Runtime.dll"); yield return Path.Combine (referenceDir, "System.Runtime.Extensions.dll"); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index ded92af4ba507..52fe64b363255 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -3317,11 +3317,23 @@ void MarkImplicitlyUsedFields (TypeDefinition type) if (type?.HasFields != true) return; - // keep fields for types with explicit layout and for enums - if (!type.IsAutoLayout || type.IsEnum) + // keep fields for types with explicit layout, for enums and for InlineArray types + if (!type.IsAutoLayout || type.IsEnum || TypeIsInlineArrayType(type)) MarkFields (type, includeStatic: type.IsEnum, reason: new DependencyInfo (DependencyKind.MemberOfType, type)); } + static bool TypeIsInlineArrayType(TypeDefinition type) + { + if (!type.IsValueType) + return false; + + foreach (var customAttribute in type.CustomAttributes) + if (customAttribute.AttributeType.IsTypeOf ("System.Runtime.CompilerServices", "InlineArrayAttribute")) + return true; + + return false; + } + protected virtual void MarkRequirementsForInstantiatedTypes (TypeDefinition type) { if (Annotations.IsInstantiated (type)) diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs index 3c5bb3cd86ab2..29622dc32365c 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs @@ -149,6 +149,12 @@ public Task GenericParameterDataFlow () return RunTest (nameof (GenericParameterDataFlow)); } + [Fact] + public Task InlineArrayDataflow () + { + return RunTest (); + } + [Fact] public Task MakeGenericDataFlow () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/InlineArraysTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/InlineArraysTests.cs new file mode 100644 index 0000000000000..e8e1717d6379a --- /dev/null +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/InlineArraysTests.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests +{ + public sealed partial class InlineArraysTests : LinkerTestBase + { + protected override string TestSuiteName => "InlineArrays"; + + [Fact] + public Task InlineArray () + { + return RunTest (nameof (InlineArray)); + } + + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/InlineArrayDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/InlineArrayDataflow.cs new file mode 100644 index 0000000000000..7269107fa7c01 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/InlineArrayDataflow.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class InlineArrayDataflow + { + public static void Main() + { + AccessPrimitiveTypeArray (); + AccessUnannotatedTypeArray (); + AccessAnnotatedTypeArray (); + } + + public int TestProperty { get; set; } + + [InlineArray (5)] + struct PrimitiveTypeArray + { + public BindingFlags value; + } + + // This case will fallback to not understanding the binding flags and will end up marking all properties + static void AccessPrimitiveTypeArray () + { + PrimitiveTypeArray a = new PrimitiveTypeArray (); + ref var item = ref a[1]; + item = BindingFlags.Public; + + typeof (InlineArrayDataflow).GetProperty (nameof (TestProperty), a[1]); + } + + [InlineArray (5)] + struct UnannotatedTypeArray + { + public Type value; + } + + // Analyzer doesn't understand inline arrays currently - so it doesn't produce a warning here + [ExpectedWarning ("IL2065", "GetProperty", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + static void AccessUnannotatedTypeArray () + { + UnannotatedTypeArray a = new UnannotatedTypeArray (); + ref var item = ref a[2]; + item = typeof (InlineArrayDataflow); + + a[2].GetProperty (nameof (TestProperty)); + } + + [InlineArray (5)] + struct AnnotatedTypeArray + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + public Type value; + } + + // Currently tracking of annotations on inline array values is not implemented + [ExpectedWarning("IL2065", "GetProperty", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + static void AccessAnnotatedTypeArray () + { + AnnotatedTypeArray a = new AnnotatedTypeArray (); + ref var item = ref a[3]; + item = typeof (InlineArrayDataflow); + + a[3].GetProperty (nameof (TestProperty)); + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/InlineArrays/InlineArray.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/InlineArrays/InlineArray.cs new file mode 100644 index 0000000000000..ce0fd7b114f04 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/InlineArrays/InlineArray.cs @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.InlineArrays +{ + [ExpectedNoWarnings] + public class InlineArray + { + public static void Main() + { + InlineArrayUsage.Test (); + CollectionLiteralsOfArrays.Test (); + } + + [Kept] + class InlineArrayUsage + { + // NativeAOT will remove most of the struct type information as it's not needed + // in the generated native code. Eventually we might come up with a better test infra to validate this. + [Kept (By = Tool.Trimmer)] + public struct StructWithFixedBuffer + { + [Kept (By = Tool.Trimmer)] + public FixedBuffer Buffer; + + [Kept (By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof(InlineArrayAttribute), By = Tool.Trimmer)] + [InlineArray (8)] + public partial struct FixedBuffer + { + [Kept (By = Tool.Trimmer)] + public int e0; + } + } + + [Kept (By = Tool.Trimmer)] + public struct StructWithAutoLayoutBuffer + { + [Kept (By = Tool.Trimmer)] + public AutoLayoutBuffer Buffer; + + [Kept (By = Tool.Trimmer)] + [KeptAttributeAttribute (typeof (InlineArrayAttribute), By = Tool.Trimmer)] + [InlineArray (8)] + [StructLayout (LayoutKind.Auto)] + public struct AutoLayoutBuffer + { + [Kept (By = Tool.Trimmer)] + public int e0; + } + } + + [Kept] + public static void Test () + { + var s = new StructWithFixedBuffer (); + s.Buffer[0] = 5; + + var sa = new StructWithAutoLayoutBuffer (); + _ = sa.Buffer[1]; + } + } + + [Kept] + [KeptMember (".cctor()")] + class CollectionLiteralsOfArrays + { + [Kept] + public static readonly ImmutableArray ImmutableValues = ["one", "two"]; + [Kept] + public static readonly string[] ArrayValues = ["one", "two"]; + + [Kept] + public static void Test() + { + _ = CollectionLiteralsOfArrays.ImmutableValues[0]; + _ = CollectionLiteralsOfArrays.ArrayValues[1]; + } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestDatabase.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestDatabase.cs index a4c00087fe55c..40740bc71125d 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -119,6 +119,11 @@ public static IEnumerable InheritanceVirtualMethodsTests () return NUnitCasesBySuiteName ("Inheritance.VirtualMethods"); } + public static IEnumerable InlineArrayTests () + { + return NUnitCasesBySuiteName ("InlineArrays"); + } + public static IEnumerable InteropTests () { return NUnitCasesBySuiteName ("Interop"); diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestSuites.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestSuites.cs index b26acccc0a18b..fd25f5cc8cef9 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -142,6 +142,12 @@ public void InheritanceVirtualMethodsTests (TestCase testCase) Run (testCase); } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.InlineArrayTests))] + public void InlineArrayTests (TestCase testCase) + { + Run (testCase); + } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.InteropTests))] public void InteropTests (TestCase testCase) { diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs index 5109942710631..c26e833d9e9f3 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompilationMetadataProvider.cs @@ -148,9 +148,11 @@ public virtual IEnumerable GetCommonReferencedAssemblies (NPath workingD yield return Path.Combine (referenceDir, "mscorlib.dll"); yield return Path.Combine (referenceDir, "System.Collections.dll"); + yield return Path.Combine (referenceDir, "System.Collections.Immutable.dll"); yield return Path.Combine (referenceDir, "System.ComponentModel.TypeConverter.dll"); yield return Path.Combine (referenceDir, "System.Console.dll"); yield return Path.Combine (referenceDir, "System.Linq.Expressions.dll"); + yield return Path.Combine (referenceDir, "System.Memory.dll"); yield return Path.Combine (referenceDir, "System.ObjectModel.dll"); yield return Path.Combine (referenceDir, "System.Runtime.dll"); yield return Path.Combine (referenceDir, "System.Runtime.Extensions.dll");