diff options
author | Jackson Schuster <36744439+jtschuster@users.noreply.github.com> | 2022-04-12 23:34:39 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-12 23:34:39 +0300 |
commit | 8c33ad3ad8cba002301cf7341ba9003379247edb (patch) | |
tree | 36a5fd293855a21df9a1f3ec3332c6a2c12a0b6c | |
parent | 805c6a7f868348bc2400fcacb4f00998bd67b60d (diff) |
Don't remove unused interfaces in library mode (#2711)
This will make sure that we keep all interfaces and all interface method implementations on such type.
* Change the solution to rely on the optimization setting only
* wip
* Add implicit interface implementation case
* Edit tests to remove issue-specific code and names
* Mark members of CollectedType as kept
* Add private interface test and simplify external interface example
* Replace early exit for non-interfaces
* License headers and use IsMethodNeededByTypeDueToPreservedScope instead of Interface specific version
* Add check for static methods before skipping virtual marking
Check for optimization before skipping marking interface methods for
PreservedScope
Add doc comments
* Add more clarifying comments, move static method check, rename method
Rename IsVirtualNeededByInstantiatedTypeDueToPreservedScope to IsOverrideNeededByInstantiatedTypeDueToPreservedScope
Added more comments describing purpose of methods
Moved the static interface method check to the method that is called on all types regardless of instantiation
* Renames and comment cleanup + tests
This doesn't change product behavior, only renamed methods and improved comments.
Added new tests for the RootLibrary:
- Added a dependency to an "copy" assembly (mainly because I can define a static interface in it)
- Added more combinations to the interfaces/classes in the test
- Since this uses static interface methods I had to enable "preview" language features for the test project and for the test infra.
* More tests for interface behavior
Co-authored-by: vitek-karas <vitek.karas@microsoft.com>
Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
9 files changed, 531 insertions, 18 deletions
diff --git a/Directory.Build.props b/Directory.Build.props index c6b56b26a..ed94e7f27 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,7 +29,7 @@ </PropertyGroup> <PropertyGroup> - <LangVersion>latest</LangVersion> + <LangVersion>preview</LangVersion> <AnalysisLevel>latest</AnalysisLevel> </PropertyGroup> </Project> diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index cafd61983..2c9c7e038 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -584,11 +584,15 @@ namespace Mono.Linker.Steps foreach ((var type, var scope) in typesWithInterfaces) { // Exception, types that have not been flagged as instantiated yet. These types may not need their interfaces even if the // interface type is marked - if (!Annotations.IsInstantiated (type) && !Annotations.IsRelevantToVariantCasting (type)) + // UnusedInterfaces optimization is turned off mark all interface implementations + bool unusedInterfacesOptimizationEnabled = Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type); + if (!Annotations.IsInstantiated (type) && !Annotations.IsRelevantToVariantCasting (type) && + unusedInterfacesOptimizationEnabled) continue; - using (ScopeStack.PushScope (scope)) + using (ScopeStack.PushScope (scope)) { MarkInterfaceImplementations (type); + } } } @@ -1719,6 +1723,9 @@ namespace Mono.Linker.Steps } } + /// <summary> + /// Returns true if the assembly of the <paramref name="scope"></paramref> is not set to link (i.e. action=copy is set for that assembly) + /// </summary> protected virtual bool IgnoreScope (IMetadataScope scope) { AssemblyDefinition? assembly = Context.Resolve (scope); @@ -1915,7 +1922,7 @@ namespace Mono.Linker.Steps MarkGenericParameterProvider (type); // There are a number of markings we can defer until later when we know it's possible a reference type could be instantiated - // For example, if no instance of a type exist, then we don't need to mark the interfaces on that type + // For example, if no instance of a type exist, then we don't need to mark the interfaces on that type -- Note this is not true for static interfaces // However, for some other types there is no benefit to deferring if (type.IsInterface) { // There's no benefit to deferring processing of an interface type until we know a type implementing that interface is marked @@ -1939,12 +1946,13 @@ namespace Mono.Linker.Steps MarkRequirementsForInstantiatedTypes (type); } + // Save for later once we know which interfaces are marked and then determine which interface implementations and methods to keep if (type.HasInterfaces) _typesWithInterfaces.Add ((type, ScopeStack.CurrentScope)); if (type.HasMethods) { - // For virtuals that must be preserved, blame the declaring type. - MarkMethodsIf (type.Methods, IsVirtualNeededByTypeDueToPreservedScope, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type)); + // For methods that must be preserved, blame the declaring type. + MarkMethodsIf (type.Methods, IsMethodNeededByTypeDueToPreservedScope, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type)); if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod) { using (ScopeStack.PopToParent ()) MarkStaticConstructor (type, new DependencyInfo (DependencyKind.CctorForType, type)); @@ -2278,9 +2286,19 @@ namespace Mono.Linker.Steps } } - bool IsVirtualNeededByTypeDueToPreservedScope (MethodDefinition method) - { - if (!method.IsVirtual) + /// <summary> + /// Returns true if any of the base methods of the <paramref name="method"/> passed is in an assembly that is not trimmed (i.e. action != trim). + /// Meant to be used to determine whether methods should be marked regardless of whether it is instantiated or not. + /// </summary> + /// <remarks> + /// When the unusedinterfaces optimization is on, this is used to mark methods that override an abstract method from a non-link assembly and must be kept. + /// When the unusedinterfaces optimization is off, this will do the same as when on but will also mark interface methods from interfaces defined in a non-link assembly. + /// If the containing type is instantiated, the caller should also use <see cref="IsMethodNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition)" /> + /// </remarks> + bool IsMethodNeededByTypeDueToPreservedScope (MethodDefinition method) + { + // Static methods may also have base methods in static interface methods. These methods are not captured by IsVirtual and must be checked separately + if (!(method.IsVirtual || method.IsStatic)) return false; var base_list = Annotations.GetBaseMethods (method); @@ -2289,8 +2307,8 @@ namespace Mono.Linker.Steps foreach (MethodDefinition @base in base_list) { // Just because the type is marked does not mean we need interface methods. - // if the type is never instantiated, interfaces will be removed - if (@base.DeclaringType.IsInterface) + // if the type is never instantiated, interfaces will be removed - but only if the optimization is enabled + if (@base.DeclaringType.IsInterface && Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, method.DeclaringType)) continue; // If the type is marked, we need to keep overrides of abstract members defined in assemblies @@ -2302,15 +2320,24 @@ namespace Mono.Linker.Steps if (IgnoreScope (@base.DeclaringType.Scope)) return true; - if (IsVirtualNeededByTypeDueToPreservedScope (@base)) + if (IsMethodNeededByTypeDueToPreservedScope (@base)) return true; } return false; } - bool IsVirtualNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method) + /// <summary> + /// Returns true if any of the base methods of <paramref name="method" /> is defined in an assembly that is not trimmed (i.e. action!=trim). + /// This is meant to be used on methods from a type that is known to be instantiated. + /// </summary> + /// <remarks> + /// This is very similar to <see cref="IsMethodNeededByTypeDueToPreservedScope (MethodDefinition)"/>, + /// but will mark methods from an interface defined in a non-link assembly regardless of the optimization, and does not handle static interface methods. + /// </remarks> + bool IsMethodNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method) { + // Any static interface methods are captured by <see cref="IsVirtualNeededByTypeDueToPreservedScope">, which should be called on all relevant methods so no need to check again here. if (!method.IsVirtual) return false; @@ -2322,7 +2349,7 @@ namespace Mono.Linker.Steps if (IgnoreScope (@base.DeclaringType.Scope)) return true; - if (IsVirtualNeededByTypeDueToPreservedScope (@base)) + if (IsMethodNeededByTypeDueToPreservedScope (@base)) return true; } @@ -3110,7 +3137,7 @@ namespace Mono.Linker.Steps protected virtual IEnumerable<MethodDefinition> GetRequiredMethodsForInstantiatedType (TypeDefinition type) { foreach (var method in type.Methods) { - if (IsVirtualNeededByInstantiatedTypeDueToPreservedScope (method)) + if (IsMethodNeededByInstantiatedTypeDueToPreservedScope (method)) yield return method; } } diff --git a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 7b604a38a..74ea1c72d 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -21,5 +21,11 @@ namespace ILLink.RoslynAnalyzer.Tests.Inheritance return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceVariants () + { + return RunTest (allowMissingWarnings: true); + } + } }
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/CopyLibrary.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/CopyLibrary.cs new file mode 100644 index 000000000..655198709 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/CopyLibrary.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.Dependencies +{ + public interface ICopyLibraryInterface + { + void CopyLibraryInterfaceMethod (); + void CopyLibraryExplicitImplementationInterfaceMethod (); + } + + public interface ICopyLibraryStaticInterface + { + static abstract void CopyLibraryStaticInterfaceMethod (); + static abstract void CopyLibraryExplicitImplementationStaticInterfaceMethod (); + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs index 6b2cbd511..1c4a08112 100644 --- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceOnUninstantiatedTypeRemoved.cs @@ -3,7 +3,6 @@ using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces { - [SetupLinkerArgument ("--disable-opt", "unusedinterfaces")] public class InterfaceOnUninstantiatedTypeRemoved { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs new file mode 100644 index 000000000..f9d54d009 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs @@ -0,0 +1,242 @@ +// 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; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Inheritance.Interfaces.Dependencies; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + [SetupCompileBefore ("copylibrary.dll", new[] { "Dependencies/CopyLibrary.cs" })] + [SetupLinkerAction ("copy", "copylibrary")] + public class InterfaceVariants + { + public static void Main () + { + Type t = typeof (UninstantiatedPublicClassWithInterface); + t = typeof (UninstantiatedPublicClassWithImplicitlyImplementedInterface); + t = typeof (UninstantiatedPublicClassWithPrivateInterface); + + UninstantiatedPublicClassWithInterface.InternalStaticInterfaceMethodUsed (); + InstantiatedClassWithInterfaces.InternalStaticInterfaceMethodUsed (); + + // Use all public interfaces - they're marked as public only to denote them as "used" + typeof (IPublicInterface).RequiresPublicMethods (); + typeof (IPublicStaticInterface).RequiresPublicMethods (); + + var a = new InstantiatedClassWithInterfaces (); + } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalStaticInterfaceWithUsedMethod))] // https://github.com/dotnet/linker/issues/2733 + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class UninstantiatedPublicClassWithInterface : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IInternalStaticInterfaceWithUsedMethod, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + internal UninstantiatedPublicClassWithInterface () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethodUsed () { } + + [Kept] + [ExpectBodyModified] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { + [Kept] + [ExpectBodyModified] + get { throw new PlatformNotSupportedException (); } + } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + [KeptInterface (typeof (IFormattable))] + public class UninstantiatedPublicClassWithImplicitlyImplementedInterface : IInternalInterface, IFormattable + { + internal UninstantiatedPublicClassWithImplicitlyImplementedInterface () { } + + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + [ExpectBodyModified] + [ExpectLocalsModified] + public string ToString (string format, IFormatProvider formatProvider) + { + return "formatted string"; + } + } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalStaticInterfaceWithUsedMethod))] // https://github.com/dotnet/linker/issues/2733 + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class InstantiatedClassWithInterfaces : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IInternalStaticInterfaceWithUsedMethod, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + [Kept] + public InstantiatedClassWithInterfaces () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethodUsed () { } + + [Kept] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + public class UninstantiatedPublicClassWithPrivateInterface : IPrivateInterface + { + internal UninstantiatedPublicClassWithPrivateInterface () { } + + void IPrivateInterface.PrivateInterfaceMethod () { } + } + + [Kept] + public interface IPublicInterface + { + [Kept] + void PublicInterfaceMethod (); + + [Kept] + void ExplicitImplementationPublicInterfaceMethod (); + } + + [Kept] + public interface IPublicStaticInterface + { + [Kept] + static abstract void PublicStaticInterfaceMethod (); + + [Kept] + static abstract void ExplicitImplementationPublicStaticInterfaceMethod (); + } + + internal interface IInternalInterface + { + void InternalInterfaceMethod (); + + void ExplicitImplementationInternalInterfaceMethod (); + } + + internal interface IInternalStaticInterface + { + static abstract void InternalStaticInterfaceMethod (); + + static abstract void ExplicitImplementationInternalStaticInterfaceMethod (); + } + + // The interface methods themselves are not used, but the implentation of these methods is + // https://github.com/dotnet/linker/issues/2733 + [Kept] + internal interface IInternalStaticInterfaceWithUsedMethod + { + [Kept] // https://github.com/dotnet/linker/issues/2733 + static abstract void InternalStaticInterfaceMethodUsed (); + } + + private interface IPrivateInterface + { + void PrivateInterfaceMethod (); + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Libraries/Dependencies/CopyLibrary.cs b/test/Mono.Linker.Tests.Cases/Libraries/Dependencies/CopyLibrary.cs new file mode 100644 index 000000000..0f31e8277 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Libraries/Dependencies/CopyLibrary.cs @@ -0,0 +1,17 @@ +// 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. + +namespace Mono.Linker.Tests.Cases.Libraries.Dependencies +{ + public interface ICopyLibraryInterface + { + void CopyLibraryInterfaceMethod (); + void CopyLibraryExplicitImplementationInterfaceMethod (); + } + + public interface ICopyLibraryStaticInterface + { + static abstract void CopyLibraryStaticInterfaceMethod (); + static abstract void CopyLibraryExplicitImplementationStaticInterfaceMethod (); + } +} diff --git a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs index 8cc979367..1534573ea 100644 --- a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs @@ -1,11 +1,18 @@ +// 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; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Runtime.Serialization; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Libraries.Dependencies; namespace Mono.Linker.Tests.Cases.Libraries { + [SetupCompileBefore ("copylibrary.dll", new[] { "Dependencies/CopyLibrary.cs" })] + [SetupLinkerAction ("copy", "copylibrary")] [SetupLinkerArgument ("-a", "test.exe", "library")] [SetupLinkerArgument ("--enable-opt", "ipconstprop")] [VerifyMetadataNames] @@ -172,6 +179,204 @@ namespace Mono.Linker.Tests.Cases.Libraries public interface I { } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalInterface))] + [KeptInterface (typeof (IInternalStaticInterface))] + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class UninstantiatedPublicClassWithInterface : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + internal UninstantiatedPublicClassWithInterface () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + [Kept] + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + [KeptInterface (typeof (IInternalInterface))] + [KeptInterface (typeof (IFormattable))] + public class UninstantiatedPublicClassWithImplicitlyImplementedInterface : IInternalInterface, IFormattable + { + internal UninstantiatedPublicClassWithImplicitlyImplementedInterface () { } + + [Kept] + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + public string ToString (string format, IFormatProvider formatProvider) + { + return "formatted string"; + } + } + + [Kept] + [KeptInterface (typeof (IEnumerator))] + [KeptInterface (typeof (IPublicInterface))] + [KeptInterface (typeof (IPublicStaticInterface))] + [KeptInterface (typeof (IInternalInterface))] + [KeptInterface (typeof (IInternalStaticInterface))] + [KeptInterface (typeof (ICopyLibraryInterface))] + [KeptInterface (typeof (ICopyLibraryStaticInterface))] + public class InstantiatedClassWithInterfaces : + IPublicInterface, + IPublicStaticInterface, + IInternalInterface, + IInternalStaticInterface, + IEnumerator, + ICopyLibraryInterface, + ICopyLibraryStaticInterface + { + [Kept] + public InstantiatedClassWithInterfaces () { } + + [Kept] + public void PublicInterfaceMethod () { } + + [Kept] + void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { } + + [Kept] + public static void PublicStaticInterfaceMethod () { } + + [Kept] + static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { } + + [Kept] + public void InternalInterfaceMethod () { } + + void IInternalInterface.ExplicitImplementationInternalInterfaceMethod () { } + + [Kept] + public static void InternalStaticInterfaceMethod () { } + + static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } + + [Kept] + bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } + + [Kept] + object IEnumerator.Current { [Kept] get { throw new PlatformNotSupportedException (); } } + + [Kept] + void IEnumerator.Reset () { } + + [Kept] + public void CopyLibraryInterfaceMethod () { } + + [Kept] + void ICopyLibraryInterface.CopyLibraryExplicitImplementationInterfaceMethod () { } + + [Kept] + public static void CopyLibraryStaticInterfaceMethod () { } + + [Kept] + static void ICopyLibraryStaticInterface.CopyLibraryExplicitImplementationStaticInterfaceMethod () { } + } + + [Kept] + [KeptInterface (typeof (IPrivateInterface))] + public class UninstantiatedPublicClassWithPrivateInterface : IPrivateInterface + { + internal UninstantiatedPublicClassWithPrivateInterface () { } + + void IPrivateInterface.PrivateInterfaceMethod () { } + } + + [Kept] + public interface IPublicInterface + { + [Kept] + void PublicInterfaceMethod (); + + [Kept] + void ExplicitImplementationPublicInterfaceMethod (); + } + + [Kept] + public interface IPublicStaticInterface + { + [Kept] + static abstract void PublicStaticInterfaceMethod (); + + [Kept] + static abstract void ExplicitImplementationPublicStaticInterfaceMethod (); + } + + [Kept] + internal interface IInternalInterface + { + void InternalInterfaceMethod (); + + void ExplicitImplementationInternalInterfaceMethod (); + } + + [Kept] + internal interface IInternalStaticInterface + { + [Kept] // https://github.com/dotnet/linker/issues/2733 + static abstract void InternalStaticInterfaceMethod (); + + static abstract void ExplicitImplementationInternalStaticInterfaceMethod (); + } + + [Kept] + private interface IPrivateInterface + { + void PrivateInterfaceMethod (); + } } internal class RootLibrary_Internal diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs index 8aeb92da8..991c1860e 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs @@ -234,7 +234,7 @@ namespace Mono.Linker.Tests.TestCasesRunner #if NETCOREAPP protected virtual NPath CompileCSharpAssemblyWithRoslyn (CompilerOptions options) { - var languageVersion = LanguageVersion.Default; + var languageVersion = LanguageVersion.Preview; var compilationOptions = new CSharpCompilationOptions ( outputKind: options.OutputPath.FileName.EndsWith (".exe") ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default |