Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check in simple end-to-end test for covariant returns. #46285

Merged
3 changes: 2 additions & 1 deletion eng/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@ function TestUsingOptimizedRunner() {
}

# Exclude out the multi-targetted netcore app projects
$dlls = $dlls | ?{ -not ($_.FullName -match ".*netcoreapp.*") }
$dlls = $dlls | ?{ -not ($_.FullName -match ".*netcoreapp3.*") }
$dlls = $dlls | ?{ -not ($_.FullName -match ".*net5.0.*") }

# Exclude out the ref assemblies
$dlls = $dlls | ?{ -not ($_.FullName -match ".*\\ref\\.*") }
Expand Down
4 changes: 2 additions & 2 deletions eng/targets/Imports.targets
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
Only generate our runtimeconfig.json files for net core apps. It's unnecessary in desktop projects
but gets included in lots of output items like VSIX.
-->
<GenerateRuntimeConfigurationFiles Condition="'$(TargetFramework)' != 'netcoreapp3.1'">false</GenerateRuntimeConfigurationFiles>
<GenerateRuntimeConfigurationFiles Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">false</GenerateRuntimeConfigurationFiles>

<!--
When building a .NET Core exe make sure to include the template runtimeconfig.json file
Expand All @@ -36,7 +36,7 @@
This condition will be evaluated multiple times in multi-targeted projects hence need to be careful
to only set in the inner builds, not the outer build where only $(TargetFrameworks) is defined.
-->
<DisableNullableWarnings Condition="'$(DisableNullableWarnings)' == '' AND $(TargetFrameworks.Contains('netcoreapp3.1')) AND '$(TargetFramework)' != '' AND '$(TargetFramework)' != 'netcoreapp3.1'">true</DisableNullableWarnings>
<DisableNullableWarnings Condition="'$(DisableNullableWarnings)' == '' AND '$(TargetFramework)' != '' AND '$(TargetFrameworkIdentifier)' != '.NETCoreApp'">true</DisableNullableWarnings>

<!--
Disable code style analyzers in "older" targets for a multi-targeted project. These analyzers don't
Expand Down
310 changes: 310 additions & 0 deletions src/Compilers/CSharp/Test/Emit/Emit/CovariantReturnTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if NETCOREAPP

using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit
{
public class CovariantReturnTests : EmitMetadataTestBase
{
private static readonly MetadataReference CorelibraryWithCovariantReturnSupport;

static CovariantReturnTests()
{
if (new CovarantReturnRuntimeOnly().ShouldSkip)
Copy link
Member

Choose a reason for hiding this comment

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

CovarantReturnRuntimeOnly [](start = 20, length = 25)

typo: Covariant

return;

const string corLibraryCore = @"
namespace System
{
public class Array
{
public static T[] Empty<T>() => throw null;
}
public class Console
{
public static void WriteLine(string message) => throw null;
}
public class Attribute { }
[Flags]
public enum AttributeTargets
{
Assembly = 0x1,
Module = 0x2,
Class = 0x4,
Struct = 0x8,
Enum = 0x10,
Constructor = 0x20,
Method = 0x40,
Property = 0x80,
Field = 0x100,
Event = 0x200,
Interface = 0x400,
Parameter = 0x800,
Delegate = 0x1000,
ReturnValue = 0x2000,
GenericParameter = 0x4000,
All = 0x7FFF
}
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets validOn) { }
public bool AllowMultiple
{
get => throw null;
set { }
}
public bool Inherited
{
get => throw null;
set { }
}
public AttributeTargets ValidOn => throw null;
}
public struct Boolean { }
public struct Byte { }
public class Delegate
{
public static Delegate CreateDelegate(Type type, object firstArgument, Reflection.MethodInfo method) => null;
}
public abstract class Enum : IComparable { }
public class Exception
{
public Exception(string message) => throw null;
}
public class FlagsAttribute : Attribute { }
public delegate T Func<out T>();
public delegate U Func<in T, out U>(T arg);
public interface IComparable { }
public interface IDisposable
{
void Dispose();
}
public struct Int16 { }
public struct Int32 { }
public struct IntPtr { }
public class MulticastDelegate : Delegate { }
public struct Nullable<T> { }
public class Object
{
public virtual string ToString() => throw null;
public virtual int GetHashCode() => throw null;
public virtual bool Equals(object other) => throw null;
}
public sealed class ParamArrayAttribute : Attribute { }
public struct RuntimeMethodHandle { }
public struct RuntimeTypeHandle { }
public class String : IComparable {
public static String Empty = null;
public override string ToString() => throw null;
public static bool operator ==(string a, string b) => throw null;
public static bool operator !=(string a, string b) => throw null;
public override bool Equals(object other) => throw null;
public override int GetHashCode() => throw null;
}
public class Type
{
public Reflection.FieldInfo GetField(string name) => null;
public static Type GetType(string name) => null;
public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null;
}
public class ValueType { }
public struct Void { }

namespace Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current
{
get;
}
bool MoveNext();
void Reset();
}
}
namespace Collections.Generic
{
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator, IDisposable
{
new T Current
{
get;
}
}
}
namespace Linq.Expressions
{
public class Expression
{
public static ParameterExpression Parameter(Type type) => throw null;
public static ParameterExpression Parameter(Type type, string name) => throw null;
public static MethodCallExpression Call(Expression instance, Reflection.MethodInfo method, params Expression[] arguments) => throw null;
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters) => throw null;
public static MemberExpression Property(Expression expression, Reflection.MethodInfo propertyAccessor) => throw null;
public static ConstantExpression Constant(object value, Type type) => throw null;
public static UnaryExpression Convert(Expression expression, Type type) => throw null;
}
public class ParameterExpression : Expression { }
public class MethodCallExpression : Expression { }
public abstract class LambdaExpression : Expression { }
public class Expression<T> : LambdaExpression { }
public class MemberExpression : Expression { }
public class ConstantExpression : Expression { }
public sealed class UnaryExpression : Expression { }
}
namespace Reflection
{
public class AssemblyVersionAttribute : Attribute
{
public AssemblyVersionAttribute(string version) { }
}
public class DefaultMemberAttribute : Attribute
{
public DefaultMemberAttribute(string name) { }
}
public abstract class MemberInfo { }
public abstract class MethodBase : MemberInfo
{
public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => throw null;
}
public abstract class MethodInfo : MethodBase
{
public virtual Delegate CreateDelegate(Type delegateType, object target) => throw null;
}
public abstract class FieldInfo : MemberInfo
{
public abstract object GetValue(object obj);
}
}
namespace Runtime.CompilerServices
{
public static class RuntimeHelpers
{
public static object GetObjectValue(object obj) => null;
}
}
}
";
const string corlibWithCovariantSupport = corLibraryCore + @"
namespace System.Runtime.CompilerServices
{
public static class RuntimeFeature
{
public const string CovariantReturnsOfClasses = nameof(CovariantReturnsOfClasses);
public const string DefaultImplementationsOfInterfaces = nameof(DefaultImplementationsOfInterfaces);
}
public sealed class PreserveBaseOverridesAttribute : Attribute { }
}
";
var compilation = CreateEmptyCompilation(new string[] {
corlibWithCovariantSupport,
@"[assembly: System.Reflection.AssemblyVersion(""4.0.0.0"")]"
}, assemblyName: "mscorlib");
compilation.VerifyDiagnostics();
CorelibraryWithCovariantReturnSupport = compilation.EmitToImageReference(options: new CodeAnalysis.Emit.EmitOptions(runtimeMetadataVersion: "v5.1"));
}

private static CSharpCompilation CreateCovariantCompilation(
string source,
CSharpCompilationOptions options = null,
IEnumerable<MetadataReference> references = null)
{
Assert.NotNull(CorelibraryWithCovariantReturnSupport);
references = (references == null) ?
new[] { CorelibraryWithCovariantReturnSupport } :
references.ToArray().Prepend(CorelibraryWithCovariantReturnSupport);
return CreateEmptyCompilation(
source,
options: options,
parseOptions: TestOptions.WithCovariantReturns,
references: references);
}

[ConditionalFact(typeof(CovarantReturnRuntimeOnly))]
public void SimpleCovariantReturnEndToEndTest()
{
var source = @"
using System;
class Base
{
public virtual object M() => ""Base.M"";
}
class Derived : Base
{
public override string M() => ""Derived.M"";
}
class Program
{
static void Main()
{
Derived d = new Derived();
Base b = d;
string s = d.M();
object o = b.M();
Console.WriteLine(s.ToString());
Console.WriteLine(o.ToString());
}
}
";
var compilation = CreateCovariantCompilation(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var expectedOutput =
@"Derived.M
Derived.M";
CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: Verification.Skipped);
}

[ConditionalFact(typeof(CovarantReturnRuntimeOnly))]
public void CovariantRuntimeHasRequiredMembers()
{
var source = @"
using System;
class Base
{
public virtual object M() => ""Base.M"";
}
class Derived : Base
{
public override string M() => ""Derived.M"";
}
class Program
{
static void Main()
{
var value = (string)Type.GetType(""System.Runtime.CompilerServices.RuntimeFeature"").GetField(""CovariantReturnsOfClasses"").GetValue(null);
if (value != ""CovariantReturnsOfClasses"")
throw new Exception(value.ToString());

var attr = Type.GetType(""System.Runtime.CompilerServices.PreserveBaseOverridesAttribute"");
if (attr == null)
throw new Exception(""missing System.Runtime.CompilerServices.PreserveBaseOverridesAttribute"");
}
}
";
var compilation = CreateCovariantCompilation(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var expectedOutput = @"";
CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: Verification.Skipped);
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>Microsoft.CodeAnalysis.CSharp.UnitTests</RootNamespace>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
<TargetFrameworks>net5.0;net472</TargetFrameworks>
Copy link
Member

@jcouv jcouv Jul 24, 2020

Choose a reason for hiding this comment

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

Just to confirm, we're intentionally keeping our other test projects targeting netcoreapp3.1? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

I suspect it is @jaredpar's intention to switch them all at some point.


In reply to: 460321746 [](ancestors = 460321746)

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup Label="Project References">
Expand Down
13 changes: 13 additions & 0 deletions src/Test/Utilities/Portable/Assert/ConditionalFactAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,19 @@ public class WindowsDesktopOnly : ExecutionCondition
public override string SkipReason => "Test only supported on Windows desktop";
}

public class CovarantReturnRuntimeOnly : ExecutionCondition
{
public override bool ShouldSkip
{
get
{
// See if the runtime supports covariant returns.
return Type.GetType("System.Runtime.CompilerServices.RuntimeFeature")?.GetField("CovariantReturnsOfClasses") == null;
}
}
public override string SkipReason => "Test only supported on runtimes that support covariant returns";
}

public class UnixLikeOnly : ExecutionCondition
{
public override bool ShouldSkip => !PathUtilities.IsUnixLikePlatform;
Expand Down
1 change: 1 addition & 0 deletions src/Tools/BuildBoss/ProjectCheckerUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ private bool CheckTargetFrameworks(TextWriter textWriter)
case "net20":
case "net472":
case "netcoreapp3.1":
case "net5.0":
continue;
}

Expand Down