diff --git a/Directory.Build.props b/Directory.Build.props
index c6b56b26ad11..ed94e7f27595 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -29,7 +29,7 @@
- latest
+ preview
latest
diff --git a/src/linker/Linker.Dataflow/ArrayValue.cs b/src/linker/Linker.Dataflow/ArrayValue.cs
index 884e5cd7e0cc..71934bc28ca8 100644
--- a/src/linker/Linker.Dataflow/ArrayValue.cs
+++ b/src/linker/Linker.Dataflow/ArrayValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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;
diff --git a/src/linker/Linker.Dataflow/FieldValue.cs b/src/linker/Linker.Dataflow/FieldValue.cs
index 41631512815e..6d20f8301c79 100644
--- a/src/linker/Linker.Dataflow/FieldValue.cs
+++ b/src/linker/Linker.Dataflow/FieldValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/linker/Linker.Dataflow/GenericParameterValue.cs b/src/linker/Linker.Dataflow/GenericParameterValue.cs
index 7f6e104b13c4..f96ae719eac6 100644
--- a/src/linker/Linker.Dataflow/GenericParameterValue.cs
+++ b/src/linker/Linker.Dataflow/GenericParameterValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/linker/Linker.Dataflow/MethodParameterValue.cs b/src/linker/Linker.Dataflow/MethodParameterValue.cs
index 02085722312a..325b02852739 100644
--- a/src/linker/Linker.Dataflow/MethodParameterValue.cs
+++ b/src/linker/Linker.Dataflow/MethodParameterValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/linker/Linker.Dataflow/MethodReturnValue.cs b/src/linker/Linker.Dataflow/MethodReturnValue.cs
index 10414a670940..f6cb423c121e 100644
--- a/src/linker/Linker.Dataflow/MethodReturnValue.cs
+++ b/src/linker/Linker.Dataflow/MethodReturnValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/linker/Linker.Dataflow/MethodThisParameterValue.cs b/src/linker/Linker.Dataflow/MethodThisParameterValue.cs
index e9ded179ba2e..c2f46d5996e1 100644
--- a/src/linker/Linker.Dataflow/MethodThisParameterValue.cs
+++ b/src/linker/Linker.Dataflow/MethodThisParameterValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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.Generic;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/linker/Linker.Dataflow/RuntimeMethodHandleValue.cs b/src/linker/Linker.Dataflow/RuntimeMethodHandleValue.cs
index 7197b5491a43..c90ecef4bbb9 100644
--- a/src/linker/Linker.Dataflow/RuntimeMethodHandleValue.cs
+++ b/src/linker/Linker.Dataflow/RuntimeMethodHandleValue.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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 ILLink.Shared.DataFlow;
using Mono.Cecil;
diff --git a/src/linker/Linker.Dataflow/SingleValueExtensions.cs b/src/linker/Linker.Dataflow/SingleValueExtensions.cs
index b136c9fef5ca..c0024c1ccc13 100644
--- a/src/linker/Linker.Dataflow/SingleValueExtensions.cs
+++ b/src/linker/Linker.Dataflow/SingleValueExtensions.cs
@@ -1,5 +1,5 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
+// 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;
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index cafd61983830..2c9c7e0386f2 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -584,11 +584,15 @@ void ProcessMarkedTypesWithInterfaces ()
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 @@ void MarkField (FieldDefinition field, in DependencyInfo reason)
}
}
+ ///
+ /// Returns true if the assembly of the is not set to link (i.e. action=copy is set for that assembly)
+ ///
protected virtual bool IgnoreScope (IMetadataScope scope)
{
AssemblyDefinition? assembly = Context.Resolve (scope);
@@ -1915,7 +1922,7 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in
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 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in
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 @@ void MarkGenericParameter (GenericParameter parameter)
}
}
- bool IsVirtualNeededByTypeDueToPreservedScope (MethodDefinition method)
- {
- if (!method.IsVirtual)
+ ///
+ /// Returns true if any of the base methods of the 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.
+ ///
+ ///
+ /// 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
+ ///
+ 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 @@ bool IsVirtualNeededByTypeDueToPreservedScope (MethodDefinition method)
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 @@ bool IsVirtualNeededByTypeDueToPreservedScope (MethodDefinition method)
if (IgnoreScope (@base.DeclaringType.Scope))
return true;
- if (IsVirtualNeededByTypeDueToPreservedScope (@base))
+ if (IsMethodNeededByTypeDueToPreservedScope (@base))
return true;
}
return false;
}
- bool IsVirtualNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method)
+ ///
+ /// Returns true if any of the base methods of 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.
+ ///
+ ///
+ /// This is very similar to ,
+ /// but will mark methods from an interface defined in a non-link assembly regardless of the optimization, and does not handle static interface methods.
+ ///
+ bool IsMethodNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method)
{
+ // Any static interface methods are captured by , which should be called on all relevant methods so no need to check again here.
if (!method.IsVirtual)
return false;
@@ -2322,7 +2349,7 @@ bool IsVirtualNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition meth
if (IgnoreScope (@base.DeclaringType.Scope))
return true;
- if (IsVirtualNeededByTypeDueToPreservedScope (@base))
+ if (IsMethodNeededByTypeDueToPreservedScope (@base))
return true;
}
@@ -3110,7 +3137,7 @@ protected virtual void MarkRequirementsForInstantiatedTypes (TypeDefinition type
protected virtual IEnumerable 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 7b604a38ad55..74ea1c72d468 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 @@ public Task InterfaceOnUninstantiatedTypeRemoved ()
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 000000000000..655198709b1c
--- /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 6b2cbd51104c..1c4a08112c3e 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 @@
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 000000000000..f9d54d009cf1
--- /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 000000000000..0f31e8277c37
--- /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 e9e7ecf29ee6..82f78a8c80bf 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]
@@ -173,6 +180,204 @@ public override string ToString ()
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 8aeb92da83ed..991c1860e4a8 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseCompiler.cs
@@ -234,7 +234,7 @@ protected virtual NPath CompileCSharpAssemblyWithDefaultCompiler (CompilerOption
#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