diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs b/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs index 66558056aa..074b5440c3 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/TypeInference.cs @@ -866,6 +866,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public override TypeKind Kind { get { return TypeKind.TypeParameter; } } + + IType ITypeParameter.GetEffectiveBaseClass(ITypeResolveContext context) + { + return KnownTypeReference.Object.Resolve(context); + } + + IEnumerable ITypeParameter.GetEffectiveInterfaceSet(ITypeResolveContext context) + { + return EmptyList.Instance; + } } #endregion diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs index d6ec9f6f43..fe7b6a7786 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CSharpAmbienceTests.cs @@ -171,7 +171,7 @@ namespace ICSharpCode.NRefactory.CSharp #endregion #region Test types - #pragma warning disable 169 + #pragma warning disable 169, 67 class Test {} diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index f2782af801..ad8a9fb826 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -177,6 +177,7 @@ + diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs new file mode 100644 index 0000000000..8c063289e3 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs @@ -0,0 +1,87 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using NUnit.Framework; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + [TestFixture] + public class TypeParameterTests + { + [Test] + public void TypeParameterDerivingFromOtherTypeParameterDoesNotInheritReferenceConstraint() + { + // class C where T : class where U : T + DefaultTypeDefinition c = new DefaultTypeDefinition(MinimalResolveContext.Instance, string.Empty, "C"); + DefaultTypeParameter t = new DefaultTypeParameter(EntityType.TypeDefinition, 0, "T"); + DefaultTypeParameter u = new DefaultTypeParameter(EntityType.TypeDefinition, 1, "U"); + c.TypeParameters.Add(t); + c.TypeParameters.Add(u); + t.HasReferenceTypeConstraint = true; + u.Constraints.Add(t); + + // At runtime, we might have T=System.ValueType and U=int, so C# can't inherit the 'class' constraint + // from one type parameter to another. + Assert.AreEqual(true, t.IsReferenceType(MinimalResolveContext.Instance)); + Assert.IsNull(u.IsReferenceType(MinimalResolveContext.Instance)); + } + + [Test] + public void ValueTypeParameterDerivingFromReferenceTypeParameter() + { + // class C where T : class where U : T + DefaultTypeDefinition c = new DefaultTypeDefinition(MinimalResolveContext.Instance, string.Empty, "C"); + DefaultTypeParameter t = new DefaultTypeParameter(EntityType.TypeDefinition, 0, "T"); + DefaultTypeParameter u = new DefaultTypeParameter(EntityType.TypeDefinition, 1, "U"); + c.TypeParameters.Add(t); + c.TypeParameters.Add(u); + t.HasReferenceTypeConstraint = true; + u.HasValueTypeConstraint = true; + u.Constraints.Add(t); + + // At runtime, we might have T=System.ValueType and U=int, so C# can't inherit the 'class' constraint + // from one type parameter to another. + Assert.AreEqual(true, t.IsReferenceType(MinimalResolveContext.Instance)); + Assert.AreEqual(false, u.IsReferenceType(MinimalResolveContext.Instance)); + } + + [Test] + public void TypeParameterDerivingFromOtherTypeParameterInheritsEffectiveBaseClass() + { + // class C where T : class where U : T + ITypeResolveContext context = CecilLoaderTests.Mscorlib; + DefaultTypeDefinition c = new DefaultTypeDefinition(CecilLoaderTests.Mscorlib, string.Empty, "C"); + DefaultTypeParameter t = new DefaultTypeParameter(EntityType.TypeDefinition, 0, "T"); + DefaultTypeParameter u = new DefaultTypeParameter(EntityType.TypeDefinition, 1, "U"); + c.TypeParameters.Add(t); + c.TypeParameters.Add(u); + t.Constraints.Add(typeof(List).ToTypeReference()); + u.Constraints.Add(t); + + // At runtime, we might have T=System.ValueType and U=int, so C# can't inherit the 'class' constraint + // from one type parameter to another. + Assert.AreEqual(true, t.IsReferenceType(context)); + Assert.AreEqual(true, u.IsReferenceType(context)); + Assert.AreEqual("System.Collections.Generic.List`1[[System.String]]", t.GetEffectiveBaseClass(context).ReflectionName); + Assert.AreEqual("System.Collections.Generic.List`1[[System.String]]", u.GetEffectiveBaseClass(context).ReflectionName); + } + } +} diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeParameter.cs index 6712b7d596..f763194a0c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeParameter.cs @@ -76,6 +76,16 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets the region where the type parameter is defined. /// DomRegion Region { get; } + + /// + /// Gets the effective base class of this type parameter. + /// + IType GetEffectiveBaseClass(ITypeResolveContext context); + + /// + /// Gets the effective interface set of this type parameter. + /// + IEnumerable GetEffectiveInterfaceSet(ITypeResolveContext context); } /// @@ -165,6 +175,18 @@ namespace ICSharpCode.NRefactory.TypeSystem DomRegion ITypeParameter.Region { get { return DomRegion.Empty; } } + + IType ITypeParameter.GetEffectiveBaseClass(ITypeResolveContext context) + { + Contract.Requires(context != null); + Contract.Ensures(Contract.Result() != null); + } + + IEnumerable ITypeParameter.GetEffectiveInterfaceSet(ITypeResolveContext context) + { + Contract.Requires(context != null); + Contract.Ensures(Contract.Result>() != null); + } } #endif } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs index 424c55cdb0..da7bb50d92 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -99,23 +99,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation case FlagValueTypeConstraint: return false; } - // protect against cyclic dependencies between type parameters - using (var busyLock = BusyManager.Enter(this)) { - if (busyLock.Success) { - foreach (ITypeReference constraintRef in this.Constraints) { - IType constraint = constraintRef.Resolve(context); - ITypeDefinition constraintDef = constraint.GetDefinition(); - // While interfaces are reference types, an interface constraint does not - // force the type parameter to be a reference type; so we need to explicitly look for classes here. - if (constraintDef != null && constraintDef.Kind == TypeKind.Class) - return true; - if (constraint is ITypeParameter) { - bool? isReferenceType = constraint.IsReferenceType(context); - if (isReferenceType.HasValue) - return isReferenceType.Value; - } + + // A type parameter is known to be a reference type if it has the reference type constraint + // or its effective base class is not object or System.ValueType. + IType baseClass = GetEffectiveBaseClass(context); + if (baseClass.Kind == TypeKind.Class) { + if (baseClass.Namespace == "System" && baseClass.TypeParameterCount == 0) { + switch (baseClass.Name) { + case "Object": + case "ValueType": + case "Enum": + return null; } } + return true; } return null; } @@ -333,6 +330,58 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return EmptyList.Instance; } + public IType GetEffectiveBaseClass(ITypeResolveContext context) + { + // protect against cyclic type parameters + using (var busyLock = BusyManager.Enter(this)) { + if (!busyLock.Success) + return SharedTypes.UnknownType; + + if (HasValueTypeConstraint) + return context.GetTypeDefinition("System", "ValueType", 0, StringComparer.Ordinal) ?? SharedTypes.UnknownType; + + List classTypeConstraints = new List(); + foreach (ITypeReference constraintRef in this.Constraints) { + IType constraint = constraintRef.Resolve(context); + if (constraint.Kind == TypeKind.Class) { + classTypeConstraints.Add(constraint); + } else if (constraint.Kind == TypeKind.TypeParameter) { + IType baseClass = ((ITypeParameter)constraint).GetEffectiveBaseClass(context); + if (baseClass.Kind == TypeKind.Class) + classTypeConstraints.Add(baseClass); + } + } + if (classTypeConstraints.Count == 0) + return KnownTypeReference.Object.Resolve(context); + // Find the derived-most type in the resulting set: + IType result = classTypeConstraints[0]; + for (int i = 1; i < classTypeConstraints.Count; i++) { + if (classTypeConstraints[i].GetDefinition().IsDerivedFrom(result.GetDefinition(), context)) + result = classTypeConstraints[i]; + } + return result; + } + } + + public IEnumerable GetEffectiveInterfaceSet(ITypeResolveContext context) + { + List result = new List(); + // protect against cyclic type parameters + using (var busyLock = BusyManager.Enter(this)) { + if (busyLock.Success) { + foreach (ITypeReference constraintRef in this.Constraints) { + IType constraint = constraintRef.Resolve(context); + if (constraint.Kind == TypeKind.Interface) { + result.Add(constraint); + } else if (constraint.Kind == TypeKind.TypeParameter) { + result.AddRange(((ITypeParameter)constraint).GetEffectiveInterfaceSet(context)); + } + } + } + } + return result.Distinct(); + } + public IEnumerable GetBaseTypes(ITypeResolveContext context) { bool hasNonInterfaceConstraint = false;