Skip to content

Commit

Permalink
Handle arrays in NormalizeTaskTypes
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Jul 14, 2016
1 parent 37c783e commit 836fafd
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 25 deletions.
Expand Up @@ -265,7 +265,6 @@ internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method,

private static Symbol GetMember(NamedTypeSymbol containingType, MemberDescriptor descriptor)
{
// PROTOTYPE(tasklike): Look on base types.
// PROTOTYPE(tasklike): Compare kind.
// PROTOTYPE(tasklike): Compare signatures.
// PROTOTYPE(tasklike): Compare constraints.
Expand Down
37 changes: 31 additions & 6 deletions src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs
Expand Up @@ -5,10 +5,6 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols
Expand Down Expand Up @@ -60,7 +56,7 @@ internal abstract partial class ArrayTypeSymbol : TypeSymbol, IArrayTypeSymbol
// Optimize for most common case - no sizes and all dimensions are zero lower bound.
if (sizes.IsDefaultOrEmpty && lowerBounds.IsDefault)
{
return new MDArray(elementType, rank, array, customModifiers);
return new MDArrayNoSizesOrBounds(elementType, rank, array, customModifiers);
}

return new MDArrayWithSizesAndBounds(elementType, rank, sizes, lowerBounds, array, customModifiers);
Expand Down Expand Up @@ -94,6 +90,8 @@ internal abstract partial class ArrayTypeSymbol : TypeSymbol, IArrayTypeSymbol
return CreateSZArray(elementType, declaringAssembly.GetSpecialType(SpecialType.System_Array), GetSZArrayInterfaces(elementType, declaringAssembly), customModifiers);
}

internal abstract ArrayTypeSymbol WithElementType(TypeSymbol elementType);

private static ImmutableArray<NamedTypeSymbol> GetSZArrayInterfaces(
TypeSymbol elementType,
AssemblySymbol declaringAssembly)
Expand Down Expand Up @@ -512,6 +510,11 @@ private sealed class SZArray : ArrayTypeSymbol
_interfaces = constructedInterfaces;
}

internal override ArrayTypeSymbol WithElementType(TypeSymbol elementType)
{
return new SZArray(elementType, BaseTypeNoUseSiteDiagnostics, _interfaces, CustomModifiers);
}

public override int Rank
{
get
Expand Down Expand Up @@ -545,7 +548,7 @@ internal override bool HasDefaultSizesAndLowerBounds
/// <summary>
/// Represents MDARRAY - multi-dimensional array (possibly of rank 1)
/// </summary>
private class MDArray : ArrayTypeSymbol
private abstract class MDArray : ArrayTypeSymbol
{
private readonly int _rank;

Expand Down Expand Up @@ -580,6 +583,23 @@ internal sealed override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiag
{
return ImmutableArray<NamedTypeSymbol>.Empty;
}
}

private sealed class MDArrayNoSizesOrBounds : MDArray
{
internal MDArrayNoSizesOrBounds(
TypeSymbol elementType,
int rank,
NamedTypeSymbol array,
ImmutableArray<CustomModifier> customModifiers)
: base(elementType, rank, array, customModifiers)
{
}

internal override ArrayTypeSymbol WithElementType(TypeSymbol elementType)
{
return new MDArrayNoSizesOrBounds(elementType, Rank, BaseTypeNoUseSiteDiagnostics, CustomModifiers);
}

internal override bool HasDefaultSizesAndLowerBounds
{
Expand Down Expand Up @@ -610,6 +630,11 @@ private sealed class MDArrayWithSizesAndBounds : MDArray
_lowerBounds = lowerBounds;
}

internal override ArrayTypeSymbol WithElementType(TypeSymbol elementType)
{
return new MDArrayWithSizesAndBounds(elementType, Rank, _sizes, _lowerBounds, BaseTypeNoUseSiteDiagnostics, CustomModifiers);
}

internal override ImmutableArray<int> Sizes
{
get
Expand Down
40 changes: 31 additions & 9 deletions src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs
Expand Up @@ -1286,7 +1286,6 @@ internal static NamedTypeSymbol GetAsyncMethodBuilderType(this NamedTypeSymbol t
Debug.Assert(type.SpecialType != SpecialType.System_Void);

// Find the public static CreateAsyncMethodBuilder method.
// PROTOTYPE(tasklike): Look on base types.
var members = type.GetMembers(WellKnownMemberNames.CreateAsyncMethodBuilder);
foreach (var member in members)
{
Expand Down Expand Up @@ -1330,13 +1329,19 @@ private static bool NormalizeTaskTypesCore(CSharpCompilation compilation, ref Ty
switch (type.Kind)
{
case SymbolKind.NamedType:
var namedType = (NamedTypeSymbol)type;
var changed = NormalizeTaskTypesCore(compilation, ref namedType);
type = namedType;
return changed;
{
var namedType = (NamedTypeSymbol)type;
var changed = NormalizeTaskTypesCore(compilation, ref namedType);
type = namedType;
return changed;
}
case SymbolKind.ArrayType:
// PROTOTYPE(tasklike): Use VisitType or similar to cover all cases.
break;
{
var arrayType = (ArrayTypeSymbol)type;
var changed = NormalizeTaskTypesCore(compilation, ref arrayType);
type = arrayType;
return changed;
}
}
return false;
}
Expand All @@ -1353,15 +1358,21 @@ private static bool NormalizeTaskTypesCore(CSharpCompilation compilation, ref Na
return false;
}
Debug.Assert(type.Arity < 2);
type = compilation.GetWellKnownType(
var taskType = compilation.GetWellKnownType(
type.Arity == 1 ?
WellKnownType.System_Threading_Tasks_Task_T :
WellKnownType.System_Threading_Tasks_Task);
if (taskType.TypeKind == TypeKind.Error)
{
// Skip if Task types are not available.
return false;
}
type = taskType;
return true;
}
else
{
var typeArgs = type.TypeArguments;
var typeArgs = type.TypeArgumentsNoUseSiteDiagnostics;
var builder = ArrayBuilder<TypeSymbol>.GetInstance();
bool hasChanged = false;
foreach (var typeArg in typeArgs)
Expand Down Expand Up @@ -1389,5 +1400,16 @@ private static bool NormalizeTaskTypesCore(CSharpCompilation compilation, ref Na
return hasChanged;
}
}

private static bool NormalizeTaskTypesCore(CSharpCompilation compilation, ref ArrayTypeSymbol arrayType)
{
var elementType = arrayType.ElementType;
if (!NormalizeTaskTypesCore(compilation, ref elementType))
{
return false;
}
arrayType = arrayType.WithElementType(elementType);
return true;
}
}
}
Expand Up @@ -549,8 +549,7 @@ public class YourTaskBuilder<T>
);
}

// PROTOTYPE(tasklike): Use VisitType or similar to cover all cases.
//[Fact]
[Fact]
public void NormalizeTaskTypes()
{
string source =
Expand All @@ -563,8 +562,9 @@ unsafe class C<T, U>
#pragma warning disable CS0169
static MyTask F0;
static MyTask<T> F1;
static A<MyTask<MyTask>[]>.B<C<int, MyTask[]>> F2;
static int* F3;
static C<MyTask, MyTask[]>[,] F2;
static A<MyTask<MyTask>>.B<C<int, MyTask>> F3;
static int* F4;
#pragma warning restore CS0169
}
struct MyTask
Expand Down Expand Up @@ -593,24 +593,33 @@ struct MyTaskMethodBuilder<T>
Assert.Equal("System.Threading.Tasks.Task<T>", type.NormalizeTaskTypes(compilation).ToTestDisplayString());

type = compilation.GetMember<FieldSymbol>("C.F2").Type;
Assert.Equal("A<MyTask<MyTask>[]>.B<C<System.Int32, MyTask[]>>", type.ToTestDisplayString());
Assert.Equal("A<System.Threading.Tasks.Task<System.Threading.Tasks.Task>[]>.B<C<System.Int32, System.Threading.Tasks.Task[]>>", type.NormalizeTaskTypes(compilation).ToTestDisplayString());
Assert.Equal("C<MyTask, MyTask[]>[,]", type.ToTestDisplayString());
Assert.Equal("C<System.Threading.Tasks.Task, System.Threading.Tasks.Task[]>[,]", type.NormalizeTaskTypes(compilation).ToTestDisplayString());

type = compilation.GetMember<FieldSymbol>("C.F3").Type;
Assert.Equal("A<MyTask<MyTask>>.B<C<System.Int32, MyTask>>", type.ToTestDisplayString());
// PROTOTYPE(tasklike): Normalize type arguments in containing types.
//Assert.Equal("A<System.Threading.Tasks.Task<System.Threading.Tasks.Task>>.B<C<System.Int32, System.Threading.Tasks.Task>>", type.NormalizeTaskTypes(compilation).ToTestDisplayString());

type = compilation.GetMember<FieldSymbol>("C.F4").Type;
Assert.Equal("System.Int32*", type.ToTestDisplayString());
Assert.Equal("System.Int32*", type.NormalizeTaskTypes(compilation).ToTestDisplayString());
}

[Fact]
public void NormalizeTaskTypes_Nested()
public void NormalizeTaskTypes_Inner()
{
string source =
@"class C<T, U>
{
#pragma warning disable CS0169
static MyTask<U> F0;
static C<U, object>.MyTask F1;
static C<U, MyTask>.MyTask F1;
static C<T, MyTask<U>>.Inner F2;
#pragma warning restore CS0169
class Inner
{
}
class MyTask
{
public static MyTaskMethodBuilder CreateAsyncMethodBuilder() => null;
Expand All @@ -634,8 +643,88 @@ class MyTaskMethodBuilder<V>
Assert.Equal("System.Threading.Tasks.Task<U>", type.NormalizeTaskTypes(compilation).ToTestDisplayString());

type = compilation.GetMember<FieldSymbol>("C.F1").Type;
Assert.Equal("C<U, System.Object>.MyTask", type.ToTestDisplayString());
Assert.Equal("C<U, C<T, U>.MyTask>.MyTask", type.ToTestDisplayString());
Assert.Equal("System.Threading.Tasks.Task", type.NormalizeTaskTypes(compilation).ToTestDisplayString());

type = compilation.GetMember<FieldSymbol>("C.F2").Type;
Assert.Equal("C<T, C<T, U>.MyTask<U>>.Inner", type.ToTestDisplayString());
// PROTOTYPE(tasklike): Normalize type arguments in containing types.
//Assert.Equal("C<T, System.Threading.Tasks.Task<U>>.Inner", type.NormalizeTaskTypes(compilation).ToTestDisplayString());
}

[Fact]
public void NormalizeTaskTypes_Outer()
{
string source =
@"class C
{
#pragma warning disable CS0169
static MyTask<MyTask.A> F0;
static MyTask<MyTask<object>>.B F1;
#pragma warning restore CS0169
}
class MyTask
{
internal class A { }
public static MyTaskMethodBuilder CreateAsyncMethodBuilder() => null;
}
class MyTask<V>
{
internal class B { }
public static MyTaskMethodBuilder<V> CreateAsyncMethodBuilder() => null;
}
class MyTaskMethodBuilder
{
}
class MyTaskMethodBuilder<V>
{
}";
var compilation = CreateCompilationWithMscorlib45(source);
compilation.VerifyDiagnostics();

var type = compilation.GetMember<FieldSymbol>("C.F0").Type;
Assert.Equal("MyTask<MyTask.A>", type.ToTestDisplayString());
Assert.Equal("System.Threading.Tasks.Task<MyTask.A>", type.NormalizeTaskTypes(compilation).ToTestDisplayString());

type = compilation.GetMember<FieldSymbol>("C.F1").Type;
Assert.Equal("MyTask<MyTask<System.Object>>.B", type.ToTestDisplayString());
// PROTOTYPE(tasklike): Normalize type arguments in containing types.
//Assert.Equal("MyTask<System.Threading.Tasks.Task<System.Object>>.B", type.NormalizeTaskTypes(compilation).ToTestDisplayString());
}

/// <summary>
/// Normalize should have no effect if System.Threading.Tasks.Task
/// and System.Threading.Tasks.Task&lt;T&gt; are not available.
/// </summary>
[Fact]
public void NormalizeTaskTypes_MissingWellKnownTypes()
{
string source =
@"class C
{
#pragma warning disable CS0169
static MyTask<MyTask> F;
#pragma warning restore CS0169
}
struct MyTask
{
public static MyTaskMethodBuilder CreateAsyncMethodBuilder() => new MyTaskMethodBuilder();
}
struct MyTask<T>
{
public static MyTaskMethodBuilder<T> CreateAsyncMethodBuilder() => new MyTaskMethodBuilder<T>();
}
struct MyTaskMethodBuilder
{
}
struct MyTaskMethodBuilder<T>
{
}";
var compilation = CreateCompilation(source, references: new[] { MscorlibRef_v20 });
compilation.VerifyDiagnostics();
var type = compilation.GetMember<FieldSymbol>("C.F").Type;
Assert.Equal("MyTask<MyTask>", type.ToTestDisplayString());
Assert.Equal("MyTask<MyTask>", type.NormalizeTaskTypes(compilation).ToTestDisplayString());
}

[Fact]
Expand Down

0 comments on commit 836fafd

Please sign in to comment.