Browse Source

Started implementing type inference.

newNRvisualizers
Daniel Grunwald 16 years ago
parent
commit
fe78216ca4
  1. 7
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs
  2. 25
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
  3. 52
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/SimpleNameLookupTests.cs
  4. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  5. 6
      ICSharpCode.NRefactory/CSharp/Dom/GeneralScope/NamespaceDeclaration.cs
  6. 11
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  7. 2
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  8. 2
      ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs
  9. 689
      ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs
  10. 8
      ICSharpCode.NRefactory/CSharp/Resolver/UsingScope.cs
  11. 1
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  12. 4
      ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs
  13. 4
      ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

7
ICSharpCode.NRefactory.Tests/CSharp/Resolver/CastTests.cs

@ -46,5 +46,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -46,5 +46,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
AssertError(typeof(string), resolver.ResolveCast(ResolveType(typeof(string)), MakeConstant(1)));
}
[Test]
public void OverflowingCastToEnum()
{
resolver.CheckForOverflow = true;
AssertError(typeof(StringComparison), resolver.ResolveCast(ResolveType(typeof(StringComparison)), MakeConstant(long.MaxValue)));
}
}
}

25
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs

@ -20,6 +20,31 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -20,6 +20,31 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public virtual void SetUp()
{
resolver = new CSharpResolver(CecilLoaderTests.Mscorlib);
resolver.UsingScope = MakeUsingScope("");
}
protected UsingScope MakeUsingScope(string namespaceName)
{
UsingScope u = new UsingScope(mscorlib);
if (!string.IsNullOrEmpty(namespaceName)) {
foreach (string element in namespaceName.Split('.')) {
u = new UsingScope(u, string.IsNullOrEmpty(u.NamespaceName) ? element : u.NamespaceName + "." + element);
}
}
return u;
}
/// <summary>
/// Adds a using to the current top-level using scope.
/// </summary>
protected void AddUsing(string namespaceName)
{
string[] nameParts = namespaceName.Split('.');
ITypeOrNamespaceReference r = new SimpleTypeOrNamespaceReference(nameParts[0], new ITypeReference[0], resolver.CurrentTypeDefinition, resolver.UsingScope, true);
for (int i = 1; i < nameParts.Length; i++) {
throw new NotImplementedException();
}
resolver.UsingScope.Usings.Add(r);
}
protected IType ResolveType(Type type)

52
ICSharpCode.NRefactory.Tests/CSharp/Resolver/SimpleNameLookupTests.cs

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using NUnit.Framework;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
[TestFixture]
public class SimpleNameLookupTests : ResolverTestBase
{
[Test]
public void SimpleNameLookupWithoutContext()
{
// nothing should be found without specifying any UsingScope - however, the resolver also must not crash
resolver.UsingScope = null;
Assert.IsTrue(resolver.ResolveSimpleName("System", new IType[0]).IsError);
}
[Test]
public void SimpleNamespaceLookup()
{
NamespaceResolveResult nrr = (NamespaceResolveResult)resolver.ResolveSimpleName("System", new IType[0]);
Assert.AreEqual("System", nrr.NamespaceName);
Assert.AreSame(SharedTypes.UnknownType, nrr.Type);
}
[Test]
public void NamespaceInParentNamespaceLookup()
{
resolver.UsingScope = MakeUsingScope("System.Collections.Generic");
NamespaceResolveResult nrr = (NamespaceResolveResult)resolver.ResolveSimpleName("Text", new IType[0]);
Assert.AreEqual("System.Text", nrr.NamespaceName);
}
[Test]
public void NamespacesAreNotImported()
{
AddUsing("System");
Assert.IsTrue(resolver.ResolveSimpleName("Collections", new IType[0]).IsError);
}
[Test]
public void ImportedType()
{
AddUsing("System");
TypeResolveResult trr = (TypeResolveResult)resolver.ResolveSimpleName("String", new IType[0]);
Assert.AreEqual("System.String", trr.Type.FullName);
}
}
}

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -49,6 +49,7 @@ @@ -49,6 +49,7 @@
<Compile Include="CSharp\Resolver\ConversionsTest.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionTests.cs" />
<Compile Include="CSharp\Resolver\ResolverTestBase.cs" />
<Compile Include="CSharp\Resolver\SimpleNameLookupTests.cs" />
<Compile Include="CSharp\Resolver\UnaryOperatorTests.cs" />
<Compile Include="FormattingTests\TestBraceStlye.cs" />
<Compile Include="FormattingTests\TestFormattingBugs.cs" />

6
ICSharpCode.NRefactory/CSharp/Dom/GeneralScope/NamespaceDeclaration.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// NamespaceDeclaration.cs
//
// Author:
@ -55,9 +55,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -55,9 +55,9 @@ namespace ICSharpCode.NRefactory.CSharp
public static string BuildQualifiedName (string name1, string name2)
{
if (string.IsNullOrEmpty (name1))
return name1;
if (string.IsNullOrEmpty (name2))
return name2;
if (string.IsNullOrEmpty (name2))
return name1;
return name1 + "." + name2;
}

11
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -113,7 +113,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -113,7 +113,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
ITypeDefinition IEntity.DeclaringTypeDefinition {
get { return null; }
get { throw new NotSupportedException(); }
}
IType IMember.DeclaringType {
@ -185,7 +185,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -185,7 +185,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
IProjectContent IEntity.ProjectContent {
get { return null; }
get { throw new NotSupportedException(); }
}
string INamedElement.FullName {
@ -1381,7 +1381,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1381,7 +1381,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
MemberLookup lookup = new MemberLookup(context, this.CurrentTypeDefinition, this.UsingScope.ProjectContent);
// look in current type definitions
for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) {
if (k == 0) {
@ -1393,9 +1392,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1393,9 +1392,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
if (lookupMode == SimpleNameLookupMode.Expression || lookupMode == SimpleNameLookupMode.InvocationTarget) {
// TODO: perform member lookup within the type t
MemberLookup lookup = new MemberLookup(context, t, t.ProjectContent);
ResolveResult r = lookup.Lookup(t, identifier, typeArguments, lookupMode == SimpleNameLookupMode.InvocationTarget);
if (!(r is UnknownMemberResolveResult))
if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult
return r;
} else {
// TODO: perform member lookup within the type t, restricted to finding types
@ -1512,7 +1511,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1512,7 +1511,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (target.Type == SharedTypes.Dynamic)
return DynamicResult;
MemberLookup lookup = new MemberLookup(context, this.CurrentTypeDefinition, this.UsingScope.ProjectContent);
MemberLookup lookup = new MemberLookup(context, this.CurrentTypeDefinition, this.UsingScope != null ? this.UsingScope.ProjectContent : null);
return lookup.Lookup(target.Type, identifier, typeArguments, isInvocationTarget);
}
#endregion

2
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -11,7 +11,7 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -11,7 +11,7 @@ using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Description of OverloadResolution.
/// C# overload resolution (C# 4.0 spec: §7.5).
/// </summary>
public class OverloadResolution
{

2
ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -33,7 +33,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult DoResolve(ITypeResolveContext context)
{
CSharpResolver r = new CSharpResolver(context);
r.CurrentTypeDefinition = parentTypeDefinition.GetCompoundClass();
r.CurrentTypeDefinition = parentTypeDefinition != null ? parentTypeDefinition.GetCompoundClass() : null;
r.UsingScope = parentUsingScope;
IType[] typeArgs = new IType[typeArguments.Count];
for (int i = 0; i < typeArgs.Length; i++) {

689
ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs

@ -0,0 +1,689 @@ @@ -0,0 +1,689 @@
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Implements C# 4.0 Type Inference (§7.5.2).
/// </summary>
public class TypeInference
{
readonly ITypeResolveContext context;
public TypeInference(ITypeResolveContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public IType[] InferTypeArguments(IList<ITypeParameter> typeParameters, IList<ResolveResult> arguments, IList<IType> parameterTypes, out bool success)
{
this.typeParameters = new TP[typeParameters.Count];
for (int i = 0; i < this.typeParameters.Length; i++) {
if (i != typeParameters[i].Index)
throw new ArgumentException("Type parameter has wrong index");
this.typeParameters[i] = new TP(typeParameters[i]);
}
this.parameterTypes = new IType[Math.Min(arguments.Count, parameterTypes.Count)];
this.arguments = new ResolveResult[this.parameterTypes.Length];
for (int i = 0; i < this.parameterTypes.Length; i++) {
if (arguments[i] == null || parameterTypes[i] == null)
throw new ArgumentNullException();
this.arguments[i] = arguments[i];
this.parameterTypes[i] = parameterTypes[i];
}
PhaseOne();
success = PhaseTwo();
return this.typeParameters.Select(tp => tp.FixedTo ?? SharedTypes.UnknownType).ToArray();
}
TP[] typeParameters;
IType[] parameterTypes;
ResolveResult[] arguments;
sealed class TP
{
public readonly HashSet<IType> LowerBounds = new HashSet<IType>();
public readonly HashSet<IType> UpperBounds = new HashSet<IType>();
public readonly ITypeParameter TypeParameter;
public IType FixedTo;
public bool Fixed { // TODO: rename to IsFixed
get { return FixedTo != null; }
}
public bool HasBounds {
get { return LowerBounds.Count > 0 || UpperBounds.Count > 0; }
}
public TP(ITypeParameter typeParameter)
{
if (typeParameter == null)
throw new ArgumentNullException("typeParameter");
this.TypeParameter = typeParameter;
}
public override string ToString()
{
return TypeParameter.Name;
}
}
sealed class OccursInVisitor : TypeVisitor
{
readonly TP[] tp;
public readonly bool[] Occurs;
public OccursInVisitor(TypeInference typeInference)
{
this.tp = typeInference.typeParameters;
this.Occurs = new bool[tp.Length];
}
public override IType VisitTypeParameter(ITypeParameter type)
{
int index = type.Index;
if (index < tp.Length && tp[index].TypeParameter == type)
Occurs[index] = true;
return base.VisitTypeParameter(type);
}
}
void PhaseOne()
{
// C# 4.0 spec: §7.5.2.1 The first phase
Log("Phase One");
for (int i = 0; i < arguments.Length; i++) {
ResolveResult Ei = arguments[i];
IType Ti = parameterTypes[i];
// TODO: what if Ei is an anonymous function?
IType U = Ei.Type;
if (U != SharedTypes.UnknownType) {
if (Ti is ByReferenceType) {
MakeExactInference(Ei.Type, Ti);
} else {
MakeLowerBoundInference(Ei.Type, Ti);
}
}
}
}
bool PhaseTwo()
{
// C# 4.0 spec: §7.5.2.2 The second phase
Log("Phase Two");
// All unfixed type variables Xi which do not depend on any Xj are fixed.
List<TP> typeParametersToFix = new List<TP>();
foreach (TP Xi in typeParameters) {
if (Xi.Fixed == false) {
if (!typeParameters.Any((TP Xj) => DependsOn(Xi, Xj))) {
typeParametersToFix.Add(Xi);
}
}
}
// If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold:
if (typeParametersToFix.Count == 0) {
foreach (TP Xi in typeParameters) {
// Xi has a non­empty set of bounds
if (!Xi.Fixed && Xi.HasBounds) {
// There is at least one type variable Xj that depends on Xi
if (typeParameters.Any((TP Xj) => DependsOn(Xj, Xi))) {
typeParametersToFix.Add(Xi);
}
}
}
}
// now fix 'em
bool errorDuringFix = false;
foreach (TP tp in typeParametersToFix) {
if (!Fix(tp))
errorDuringFix = true;
}
if (errorDuringFix)
return false;
bool unfixedTypeVariablesExist = typeParameters.Any((TP X) => X.Fixed == false);
if (typeParametersToFix.Count == 0 && unfixedTypeVariablesExist) {
// If no such type variables exist and there are still unfixed type variables, type inference fails.
return false;
} else if (!unfixedTypeVariablesExist) {
// Otherwise, if no further unfixed type variables exist, type inference succeeds.
return true;
} else {
// Otherwise, for all arguments ei with corresponding parameter type Ti
for (int i = 0; i < arguments.Length; i++) {
ResolveResult Ei = arguments[i];
IType Ti = parameterTypes[i];
// where the output types (§7.4.2.4) contain unfixed type variables Xj
// but the input types (§7.4.2.3) do not
if (OutputTypeContainsUnfixed(Ei, Ti) && !InputTypesContainsUnfixed(Ei, Ti)) {
// an output type inference (§7.4.2.6) is made for ei with type Ti.
Log("MakeOutputTypeInference for #" + i);
MakeOutputTypeInference(Ei, Ti);
}
}
// Then the second phase is repeated.
return PhaseTwo();
}
}
#region Input Types / Output Types (§7.5.2.3 + §7.5.2.4)
static readonly IType[] emptyTypeArray = new IType[0];
IType[] InputTypes(ResolveResult e, IType t)
{
// C# 4.0 spec: §7.5.2.3 Input types
/* TODO
AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null && amrt.HasImplicitlyTypedParameters || e is MethodGroupReturnType) {
IMethod m = GetDelegateOrExpressionTreeSignature(t, amrt != null && amrt.CanBeConvertedToExpressionTree);
if (m != null) {
return m.Parameters.Select(p => p.ReturnType);
}
}*/
return emptyTypeArray;
}
IType[] OutputTypes(ResolveResult e, IType t)
{
// C# 4.0 spec: §7.5.2.4 Input types
/*
AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null || e is MethodGroupReturnType) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt != null && amrt.CanBeConvertedToExpressionTree);
if (m != null) {
return new[] { m.ReturnType };
}
}
*/
return emptyTypeArray;
}
bool InputTypesContainsUnfixed(ResolveResult argument, IType parameterType)
{
return AnyTypeContainsUnfixedParameter(InputTypes(argument, parameterType));
}
bool OutputTypeContainsUnfixed(ResolveResult argument, IType parameterType)
{
return AnyTypeContainsUnfixedParameter(OutputTypes(argument, parameterType));
}
bool AnyTypeContainsUnfixedParameter(IEnumerable<IType> types)
{
OccursInVisitor o = new OccursInVisitor(this);
foreach (var type in types) {
type.AcceptVisitor(o);
}
for (int i = 0; i < typeParameters.Length; i++) {
if (!typeParameters[i].Fixed && o.Occurs[i])
return true;
}
return false;
}
#endregion
#region DependsOn (§7.5.2.5)
// C# 4.0 spec: §7.5.2.5 Dependance
bool[,] dependencyMatrix;
void CalculateDependencyMatrix()
{
int n = typeParameters.Length;
dependencyMatrix = new bool[n, n];
for (int k = 0; k < arguments.Length; k++) {
OccursInVisitor input = new OccursInVisitor(this);
OccursInVisitor output = new OccursInVisitor(this);
foreach (var type in InputTypes(arguments[k], parameterTypes[k])) {
type.AcceptVisitor(input);
}
foreach (var type in OutputTypes(arguments[k], parameterTypes[k])) {
type.AcceptVisitor(output);
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dependencyMatrix[i, j] = input.Occurs[j] && output.Occurs[i];
}
}
}
// calculate transitive closure using Warshall's algorithm:
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dependencyMatrix[i, j]) {
for (int k = 0; k < n; k++) {
if (dependencyMatrix[j, k])
dependencyMatrix[i, k] = true;
}
}
}
}
}
bool DependsOn(TP x, TP y)
{
if (dependencyMatrix == null)
CalculateDependencyMatrix();
// x depends on y
return dependencyMatrix[x.TypeParameter.Index, y.TypeParameter.Index];
}
#endregion
#region MakeOutputTypeInference (§7.5.2.6)
void MakeOutputTypeInference(ResolveResult e, IType t)
{
// If E is an anonymous function with inferred return type U (§7.5.2.12) and T is a delegate type or expression
// tree type with return type Tb, then a lower-bound inference (§7.5.2.9) is made from U to Tb.
/* TODO AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt.CanBeConvertedToExpressionTree);
if (m != null) {
IReturnType inferredReturnType;
if (amrt.HasParameterList && amrt.MethodParameters.Count == m.Parameters.Count) {
var inferredParameterTypes = m.Parameters.Select(p => SubstituteFixedTypes(p.ReturnType)).ToArray();
inferredReturnType = amrt.ResolveReturnType(inferredParameterTypes);
} else {
inferredReturnType = amrt.ResolveReturnType();
}
MakeLowerBoundInference(inferredReturnType, m.ReturnType);
return;
}
}*/
// Otherwise, if E is a method group and T is a delegate type or expression tree type
// with parameter types T1…Tk and return type Tb, and overload resolution
// of E with the types T1…Tk yields a single method with return type U, then a lower­-bound
// inference is made from U to Tb.
MethodGroupResolveResult mgrr = e as MethodGroupResolveResult;
if (mgrr != null) {
throw new NotImplementedException();
}
// Otherwise, if E is an expression with type U, then a lower-bound inference is made from U to T.
if (e.Type != SharedTypes.UnknownType) {
MakeLowerBoundInference(e.Type, t);
}
}
#endregion
void MakeExplicitParameterTypeInference(ResolveResult e, IType t)
{
// C# 4.0 spec: §7.5.2.7 Explicit parameter type inferences
throw new NotImplementedException();
/*AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null && amrt.HasParameterList) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt.CanBeConvertedToExpressionTree);
if (m != null && amrt.MethodParameters.Count == m.Parameters.Count) {
for (int i = 0; i < amrt.MethodParameters.Count; i++) {
MakeExactInference(amrt.MethodParameters[i].ReturnType, m.Parameters[i].ReturnType);
}
}
}*/
}
#region MakeExactInference
/// <summary>
/// Make exact inference from U to V.
/// C# 4.0 spec: 7.5.2.8 Exact inferences
/// </summary>
void MakeExactInference(IType U, IType V)
{
Log(" MakeExactInference from " + U + " to " + V);
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.Fixed == false) {
Log(" Add exact bound '" + U + "' to " + tp);
tp.LowerBounds.Add(U);
tp.UpperBounds.Add(U);
return;
}
// Handle by reference types:
ByReferenceType brU = U as ByReferenceType;
ByReferenceType brV = V as ByReferenceType;
if (brU != null && brV != null) {
MakeExactInference(brU.ElementType, brV.ElementType);
return;
}
// Handle array types:
ArrayType arrU = U as ArrayType;
ArrayType arrV = V as ArrayType;
if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) {
MakeExactInference(arrU.ElementType, arrV.ElementType);
return;
}
// Handle parameterized type:
ParameterizedType pU = U as ParameterizedType;
ParameterizedType pV = V as ParameterizedType;
if (pU != null && pV != null
&& object.Equals(pU.GetDefinition(), pV.GetDefinition())
&& pU.TypeParameterCount == pV.TypeParameterCount)
{
for (int i = 0; i < pU.TypeParameterCount; i++) {
MakeExactInference(pU.TypeArguments[i], pV.TypeArguments[i]);
}
}
}
TP GetTPForType(IType v)
{
ITypeParameter p = v as ITypeParameter;
if (p != null) {
int index = p.Index;
if (index < typeParameters.Length && typeParameters[index].TypeParameter == p)
return typeParameters[index];
}
return null;
}
#endregion
#region MakeLowerBoundInference
/// <summary>
/// Make lower bound inference from U to V.
/// C# 4.0 spec: §7.5.2.9 Lower-bound inferences
/// </summary>
void MakeLowerBoundInference(IType U, IType V)
{
Log(" MakeLowerBoundInference from " + U + " to " + V);
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.Fixed == false) {
Log(" Add lower bound '" + U + "' to " + tp);
tp.LowerBounds.Add(U);
return;
}
// Handle array types:
ArrayType arrU = U as ArrayType;
ArrayType arrV = V as ArrayType;
ParameterizedType pV = V as ParameterizedType;
if (arrU != null && (arrV != null && arrU.Dimensions == arrV.Dimensions
|| IsIEnumerableCollectionOrList(pV) && arrU.Dimensions == 1))
{
MakeLowerBoundInference(arrU.ElementType, arrV.ElementType);
return;
}
// Handle parameterized types:
if (pV != null) {
ParameterizedType uniqueBaseType = null;
foreach (IType baseU in U.GetAllBaseTypes(context)) {
ParameterizedType pU = baseU as ParameterizedType;
if (pU != null && object.Equals(pU.GetDefinition(), pV.GetDefinition()) && pU.TypeParameterCount == pV.TypeParameterCount) {
if (uniqueBaseType == null)
uniqueBaseType = pU;
else
return; // cannot make an inference because it's not unique
}
}
if (uniqueBaseType != null) {
for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) {
IType Ui = uniqueBaseType.TypeArguments[i];
IType Vi = pV.TypeArguments[i];
if (Ui.IsReferenceType == true) {
// look for variance
ITypeParameter Xi = pV.GetDefinition().TypeParameters[i];
switch (Xi.Variance) {
case VarianceModifier.Covariant:
MakeLowerBoundInference(Ui, Vi);
break;
case VarianceModifier.Contravariant:
MakeUpperBoundInference(Ui, Vi);
break;
default: // invariant
MakeExactInference(Ui, Vi);
break;
}
} else {
// not known to be a reference type
MakeExactInference(Ui, Vi);
}
}
}
}
}
static bool IsIEnumerableCollectionOrList(ParameterizedType rt)
{
if (rt == null || rt.TypeParameterCount != 1)
return false;
switch (rt.GetDefinition().FullName) {
case "System.Collections.Generic.IList":
case "System.Collections.Generic.ICollection":
case "System.Collections.Generic.IEnumerable":
return true;
default:
return false;
}
}
#endregion
#region MakeUpperBoundInference
/// <summary>
/// Make upper bound inference from U to V.
/// C# 4.0 spec: §7.5.2.10 Upper-bound inferences
/// </summary>
void MakeUpperBoundInference(IType U, IType V)
{
Log(" MakeUpperBoundInference from " + U + " to " + V);
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.Fixed == false) {
Log(" Add upper bound '" + U + "' to " + tp);
tp.UpperBounds.Add(U);
return;
}
// Handle array types:
ArrayType arrU = U as ArrayType;
ArrayType arrV = V as ArrayType;
ParameterizedType pU = U as ParameterizedType;
if (arrV != null && (arrU != null && arrU.Dimensions == arrV.Dimensions
|| IsIEnumerableCollectionOrList(pU) && arrV.Dimensions == 1))
{
MakeUpperBoundInference(arrU.ElementType, arrV.ElementType);
return;
}
// Handle parameterized types:
if (pU != null) {
ParameterizedType uniqueBaseType = null;
foreach (IType baseV in V.GetAllBaseTypes(context)) {
ParameterizedType pV = baseV as ParameterizedType;
if (pV != null && object.Equals(pU.GetDefinition(), pV.GetDefinition()) && pU.TypeParameterCount == pV.TypeParameterCount) {
if (uniqueBaseType == null)
uniqueBaseType = pV;
else
return; // cannot make an inference because it's not unique
}
}
if (uniqueBaseType != null) {
for (int i = 0; i < uniqueBaseType.TypeParameterCount; i++) {
IType Ui = pU.TypeArguments[i];
IType Vi = uniqueBaseType.TypeArguments[i];
if (Vi.IsReferenceType == true) {
// look for variance
ITypeParameter Xi = pU.GetDefinition().TypeParameters[i];
switch (Xi.Variance) {
case VarianceModifier.Covariant:
MakeUpperBoundInference(Ui, Vi);
break;
case VarianceModifier.Contravariant:
MakeLowerBoundInference(Ui, Vi);
break;
default: // invariant
MakeExactInference(Ui, Vi);
break;
}
} else {
// not known to be a reference type
MakeExactInference(Ui, Vi);
}
}
}
}
}
#endregion
#region Fixing
bool Fix(TP tp)
{
Log("Trying to fix " + tp);
Debug.Assert(!tp.Fixed);
Log(" Lower bounds: ", tp.LowerBounds);
Log(" Upper bounds: ", tp.UpperBounds);
Conversions conversions = new Conversions(context);
IEnumerable<IType> candidates = tp.LowerBounds.Union(tp.UpperBounds);
// keep only the candidates that are within all bounds
candidates = candidates.Where(c => tp.LowerBounds.All(b => conversions.ImplicitConversion(b, c)));
candidates = candidates.Where(c => tp.UpperBounds.All(b => conversions.ImplicitConversion(c, b)));
candidates = candidates.ToList(); // evaluate the query only once
Log(" Candidates: ", candidates);
IType solution = null;
foreach (var c in candidates) {
if (candidates.All(o => conversions.ImplicitConversion(c, o))) {
if (solution == null)
solution = c;
else
return false; // solution is not unique
}
}
if (solution != null) {
Log(" " + tp + " was fixed to " + solution);
tp.FixedTo = solution;
return true;
} else {
return false;
}
}
#endregion
#region Finding the best common type of a set of expresssions
/// <summary>
/// Gets the best common type (C# 4.0 spec: §7.5.2.14) of a set of expressions.
/// </summary>
public IType GetBestCommonType(IList<ResolveResult> expressions, out bool success)
{
this.typeParameters = new TP[1];
typeParameters[0] = new TP(DummyTypeParameter.Instance);
this.arguments = expressions.ToArray();
this.parameterTypes = new IType[expressions.Count];
for (int i = 0; i < parameterTypes.Length; i++) {
parameterTypes[i] = DummyTypeParameter.Instance;
}
for (int i = 0; i < arguments.Length; i++) {
MakeOutputTypeInference(arguments[i], DummyTypeParameter.Instance);
}
success = Fix(typeParameters[0]);
return typeParameters[0].FixedTo ?? SharedTypes.UnknownType;
}
sealed class DummyTypeParameter : AbstractType, ITypeParameter
{
public static readonly DummyTypeParameter Instance = new DummyTypeParameter();
public override string Name {
get { return "X"; }
}
public override bool? IsReferenceType {
get { return null; }
}
public override int GetHashCode()
{
return 0;
}
public override bool Equals(IType other)
{
return this == other;
}
int ITypeParameter.Index {
get { return 0; }
}
IList<IAttribute> ITypeParameter.Attributes {
get { return EmptyList<IAttribute>.Instance; }
}
IEntity ITypeParameter.Parent {
get { throw new NotSupportedException(); }
}
IMethod ITypeParameter.ParentMethod {
get { throw new NotSupportedException(); }
}
ITypeDefinition ITypeParameter.ParentClass {
get { throw new NotSupportedException(); }
}
IList<ITypeReference> ITypeParameter.Constraints {
get { return EmptyList<ITypeReference>.Instance; }
}
bool ITypeParameter.HasDefaultConstructorConstraint {
get { return false; }
}
bool ITypeParameter.HasReferenceTypeConstraint {
get { return false; }
}
bool ITypeParameter.HasValueTypeConstraint {
get { return false; }
}
VarianceModifier ITypeParameter.Variance {
get { return VarianceModifier.Invariant; }
}
IType ITypeParameter.BoundTo {
get { return null; }
}
ITypeParameter ITypeParameter.UnboundTypeParameter {
get { return null; }
}
bool IFreezable.IsFrozen {
get { return true; }
}
void IFreezable.Freeze()
{
}
}
#endregion
[Conditional("DEBUG")]
static void Log(string text)
{
Debug.WriteLine(text);
}
[Conditional("DEBUG")]
static void Log<T>(string text, IEnumerable<T> lines)
{
#if DEBUG
T[] arr = lines.ToArray();
if (arr.Length == 0) {
Log(text + "<empty collection>");
} else {
Log(text + (arr[0] != null ? arr[0].ToString() : "<null>"));
for (int i = 1; i < arr.Length; i++) {
Log(new string(' ', text.Length) + (arr[i] != null ? arr[i].ToString() : "<null>"));
}
}
#endif
}
}
}

8
ICSharpCode.NRefactory/CSharp/Resolver/UsingScope.cs

@ -48,6 +48,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -48,6 +48,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
base.FreezeInternal();
}
/// <summary>
/// Creates a new root using scope.
/// </summary>
public UsingScope(IProjectContent projectContent)
{
if (projectContent == null)
@ -55,6 +58,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -55,6 +58,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.projectContent = projectContent;
}
/// <summary>
/// Creates a new nested using scope.
/// </summary>
/// <param name="parent">The parent using scope.</param>
/// <param name="namespaceName">The full namespace name.</param>
public UsingScope(UsingScope parent, string namespaceName)
{
if (parent == null)

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -166,6 +166,7 @@ @@ -166,6 +166,7 @@
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ResolveResult.cs" />
<Compile Include="CSharp\Resolver\SimpleTypeOrNamespaceReference.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Resolver\TypeResolveResult.cs" />
<Compile Include="CSharp\Resolver\UnknownMemberResolveResult.cs" />
<Compile Include="CSharp\Resolver\UsingScope.cs" />

4
ICSharpCode.NRefactory/TypeSystem/Implementation/VoidTypeDefinition.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@

// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;

4
ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

@ -334,7 +334,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -334,7 +334,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
throw new ArgumentNullException("typeArguments");
ITypeReference[] typeArgs = typeArguments.ToArray();
if (genericType is ITypeDefinition && Array.TrueForAll(typeArgs, t => t is IType)) {
if (typeArgs.Length == 0) {
return genericType;
} else if (genericType is ITypeDefinition && Array.TrueForAll(typeArgs, t => t is IType)) {
IType[] ta = new IType[typeArgs.Length];
for (int i = 0; i < ta.Length; i++) {
ta[i] = (IType)typeArgs[i];

Loading…
Cancel
Save