diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index ae391f25d3..59515729af 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1167,13 +1167,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver internal readonly IParameterizedMember nonLiftedOperator; public LiftedUserDefinedOperator(IMethod nonLiftedMethod) - : base(nonLiftedMethod.DeclaringType, (IMethod)nonLiftedMethod.MemberDefinition, - EmptyList.Instance, new MakeNullableVisitor(nonLiftedMethod.Compilation)) + : base(nonLiftedMethod, TypeParameterSubstitution.Identity) { this.nonLiftedOperator = nonLiftedMethod; + var substitution = new MakeNullableVisitor(nonLiftedMethod.Compilation); + this.Parameters = base.CreateParameters(substitution); // Comparison operators keep the 'bool' return type even when lifted. if (IsComparisonOperator(nonLiftedMethod)) this.ReturnType = nonLiftedMethod.ReturnType; + else + this.ReturnType = nonLiftedMethod.ReturnType.AcceptVisitor(substitution); } public IList NonLiftedParameters { @@ -1726,13 +1729,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (typeArguments != null && typeArguments.Count > 0) { if (method.TypeParameters.Count != typeArguments.Count) continue; - SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments); + SpecializedMethod sm = new SpecializedMethod(method, new TypeParameterSubstitution(null, typeArguments)); if (IsEligibleExtensionMethod(targetType, method, false, out inferredTypes)) outputGroup.Add(sm); } else { if (IsEligibleExtensionMethod(targetType, method, true, out inferredTypes)) { if (substituteInferredTypes && inferredTypes != null) { - outputGroup.Add(new SpecializedMethod(method.DeclaringType, method, inferredTypes)); + outputGroup.Add(new SpecializedMethod(method, new TypeParameterSubstitution(null, inferredTypes))); } else { outputGroup.Add(method); } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 22e39eccc2..45754dff3d 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -164,7 +164,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.TargetType, method, true, out inferredTypes)) { if (substituteInferredTypes && inferredTypes != null) { - outputGroup.Add(new SpecializedMethod(method.DeclaringType, method, inferredTypes)); + outputGroup.Add(new SpecializedMethod(method, new TypeParameterSubstitution(null, inferredTypes))); } else { outputGroup.Add(method); } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs index c5974b0de6..cfde464100 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs @@ -807,7 +807,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return null; IMethod method = bestCandidate.Member as IMethod; if (method != null && method.TypeParameters.Count > 0) { - return new SpecializedMethod(method.DeclaringType, (IMethod)method.MemberDefinition, bestCandidate.InferredTypes); + SpecializedMethod sm = method as SpecializedMethod; + if (sm != null) { + // Do not compose the substitutions, but merge them. + // This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce + return new SpecializedMethod( + (IMethod)method.MemberDefinition, + new TypeParameterSubstitution(sm.Substitution.ClassTypeArguments, bestCandidate.InferredTypes)); + } else { + return new SpecializedMethod(method, new TypeParameterSubstitution(null, bestCandidate.InferredTypes)); + } } else { return bestCandidate.Member; } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs index 982fff8486..3d47cc88bf 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs @@ -260,5 +260,18 @@ class Test { Assert.AreEqual("Test", rr.Type.ReflectionName); Assert.AreEqual(5, rr.InitializerStatements.Count); } + + [Test] + public void CreateGeneric() + { + string program = @"using System; +class Test where T : new() { + object x = $new T()$; +}"; + var rr = Resolve(program); + Assert.IsFalse(rr.IsError); + Assert.AreEqual(TypeKind.TypeParameter, rr.Type.Kind); + Assert.AreEqual(TypeKind.TypeParameter, rr.Member.DeclaringType.Kind); + } } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 7345240918..8365a632be 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -182,6 +182,7 @@ namespace ICSharpCode.NRefactory.TypeSystem IMethod m = testClass.Methods.Single(me => me.Name == "GetIndex"); Assert.AreEqual("T", m.TypeParameters[0].Name); Assert.AreEqual(EntityType.Method, m.TypeParameters[0].OwnerType); + Assert.AreSame(m, m.TypeParameters[0].Owner); ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First(); Assert.AreEqual("IEquatable", constraint.Name); @@ -190,6 +191,53 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]); } + [Test] + public void GetIndexSpecializedTypeParameter() + { + var testClass = GetTypeDefinition(typeof(GenericClass<,>)); + var methodDef = testClass.Methods.Single(me => me.Name == "GetIndex"); + var m = new SpecializedMethod(methodDef, new TypeParameterSubstitution( + new[] { compilation.FindType(KnownTypeCode.Int16), compilation.FindType(KnownTypeCode.Int32) }, + null + )); + + Assert.AreEqual("T", m.TypeParameters[0].Name); + Assert.AreEqual(EntityType.Method, m.TypeParameters[0].OwnerType); + Assert.AreSame(m, m.TypeParameters[0].Owner); + + ParameterizedType constraint = (ParameterizedType)m.TypeParameters[0].DirectBaseTypes.First(); + Assert.AreEqual("IEquatable", constraint.Name); + Assert.AreEqual(1, constraint.TypeParameterCount); + Assert.AreEqual(1, constraint.TypeArguments.Count); + Assert.AreSame(m.TypeParameters[0], constraint.TypeArguments[0]); + } + + [Test] + public void GetIndexDoubleSpecialization() + { + var testClass = GetTypeDefinition(typeof(GenericClass<,>)); + // GenericClass.GetIndex + var methodDef = testClass.Methods.Single(me => me.Name == "GetIndex"); + + // GenericClass.GetIndex + var m1 = new SpecializedMethod(methodDef, new TypeParameterSubstitution( + new[] { testClass.TypeParameters[1], testClass.TypeParameters[0] }, + new[] { testClass.TypeParameters[0] } + )); + // GenericClass.GetIndex + var m2 = new SpecializedMethod(m1, new TypeParameterSubstitution( + new[] { compilation.FindType(KnownTypeCode.Int32), compilation.FindType(KnownTypeCode.String) }, + null + )); + + // GenericClass.GetIndex + var m12 = new SpecializedMethod(methodDef, new TypeParameterSubstitution( + new[] { compilation.FindType(KnownTypeCode.String), compilation.FindType(KnownTypeCode.Int32) }, + new[] { compilation.FindType(KnownTypeCode.Int32) } + )); + Assert.AreEqual(m12, m2); + } + [Test] public void GenericEnum() { diff --git a/ICSharpCode.NRefactory/Editor/ITextSource.cs b/ICSharpCode.NRefactory/Editor/ITextSource.cs index 8bb028a5f4..52f7aced1d 100644 --- a/ICSharpCode.NRefactory/Editor/ITextSource.cs +++ b/ICSharpCode.NRefactory/Editor/ITextSource.cs @@ -168,7 +168,7 @@ namespace ICSharpCode.NRefactory.Editor /// /// Verions can be used to efficiently detect whether a document has changed and needs reparsing; /// or even to implement incremental parsers. - /// It is a separate class from ITextBuffer to allow the GC to collect the text buffer while + /// It is a separate class from ITextSource to allow the GC to collect the text source while /// the version checkpoint is still in use. /// public interface ITextSourceVersion diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index da3ec9f14e..c6cfb7e072 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -246,19 +246,10 @@ namespace ICSharpCode.NRefactory.TypeSystem { if (type == null) throw new ArgumentNullException("type"); - ITypeDefinition def = type.GetDefinition(); - if (def != null && def.Kind == TypeKind.Delegate) { - foreach (IMember member in def.Members) { - if (member.Name == "Invoke" && member is IMethod) { - ParameterizedType pt = type as ParameterizedType; - if (pt != null) { - return new SpecializedMethod(pt, (IMethod)member); - } - return (IMethod)member; - } - } - } - return null; + if (type.Kind == TypeKind.Delegate) + return type.GetMethods(m => m.Name == "Invoke", GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + else + return null; } #endregion diff --git a/ICSharpCode.NRefactory/TypeSystem/IMember.cs b/ICSharpCode.NRefactory/TypeSystem/IMember.cs index 398ea5ff67..8b5cb1e575 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IMember.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.TypeSystem { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs index 924987b33b..bd18c9fe51 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs @@ -261,7 +261,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (this.HasDefaultConstructorConstraint || this.HasValueTypeConstraint) { if (filter == null || filter(dummyConstructor)) { var resolvedCtor = GetDummyConstructor(compilation); - IMethod m = new SpecializedMethod(this, resolvedCtor, EmptyList.Instance); + IMethod m = new SpecializedMethod(resolvedCtor, TypeParameterSubstitution.Identity) { DeclaringType = this }; return new [] { m }; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs index 6bee7d0964..d0087a010b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs @@ -781,7 +781,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation IType coClass = ComHelper.GetCoClass(this); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { - return coClass.GetConstructors(filter, options).Select(m => new SpecializedMethod(this, m)); + return coClass.GetConstructors(filter, options) + .Select(m => new SpecializedMethod(m, TypeParameterSubstitution.Identity) { DeclaringType = this }); } } return EmptyList.Instance; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs index 0ee77ad74a..18d072b53b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetMembersHelper.cs @@ -132,7 +132,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation else substitution = new TypeParameterSubstitution(null, methodTypeArguments); } - yield return new SpecializedMethod(baseType, m, methodTypeArguments, substitution); + yield return new SpecializedMethod(m, substitution); } } else { foreach (IMethod m in declaredMethods) { @@ -162,7 +162,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); - return declaredCtors.Select(m => new SpecializedMethod(pt, m, null, substitution)); + return declaredCtors.Select(m => new SpecializedMethod(m, substitution) { DeclaringType = pt }); } else { return declaredCtors; } @@ -189,7 +189,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); - return declaredProperties.Select(m => new SpecializedProperty(pt, m, substitution)); + return declaredProperties.Select(m => new SpecializedProperty(m, substitution) { DeclaringType = pt }); } else { return declaredProperties; } @@ -216,7 +216,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); - return declaredFields.Select(m => new SpecializedField(pt, m, substitution)); + return declaredFields.Select(m => new SpecializedField(m, substitution) { DeclaringType = pt }); } else { return declaredFields; } @@ -243,7 +243,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ParameterizedType pt = baseType as ParameterizedType; if (pt != null) { var substitution = pt.GetSubstitution(); - return declaredEvents.Select(m => new SpecializedEvent(pt, m, substitution)); + return declaredEvents.Select(m => new SpecializedEvent(m, substitution) { DeclaringType = pt }); } else { return declaredEvents; } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs index db0f39776e..bab18349c8 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedEvent.cs @@ -27,18 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { readonly IEvent eventDefinition; - public SpecializedEvent(IType declaringType, IEvent eventDefinition) - : base(declaringType, eventDefinition) + public SpecializedEvent(IEvent eventDefinition, TypeParameterSubstitution substitution) + : base(eventDefinition) { - this.eventDefinition = eventDefinition; - Initialize(GetSubstitution(declaringType)); - } - - internal SpecializedEvent(IType declaringType, IEvent eventDefinition, TypeVisitor substitution) - : base(declaringType, eventDefinition) - { - this.eventDefinition = eventDefinition; - Initialize(substitution); + AddSubstitution(substitution); + this.eventDefinition = (IEvent)base.MemberDefinition; } public bool CanAdd { @@ -53,16 +46,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return eventDefinition.CanInvoke; } } + IMethod addAccessor, removeAccessor, invokeAccessor; + public IMethod AddAccessor { - get { return WrapAccessor(eventDefinition.AddAccessor); } + get { return WrapAccessor(ref this.addAccessor, eventDefinition.AddAccessor); } } public IMethod RemoveAccessor { - get { return WrapAccessor(eventDefinition.RemoveAccessor); } + get { return WrapAccessor(ref this.removeAccessor, eventDefinition.RemoveAccessor); } } public IMethod InvokeAccessor { - get { return WrapAccessor(eventDefinition.InvokeAccessor); } + get { return WrapAccessor(ref this.invokeAccessor, eventDefinition.InvokeAccessor); } } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs index 5e26d2cdec..d79d1b3ab2 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedField.cs @@ -27,18 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { readonly IField fieldDefinition; - public SpecializedField(IType declaringType, IField fieldDefinition) - : base(declaringType, fieldDefinition) + public SpecializedField(IField fieldDefinition, TypeParameterSubstitution substitution) + : base(fieldDefinition) { - this.fieldDefinition = fieldDefinition; - Initialize(GetSubstitution(declaringType)); - } - - internal SpecializedField(IType declaringType, IField fieldDefinition, TypeVisitor substitution) - : base(declaringType, fieldDefinition) - { - this.fieldDefinition = fieldDefinition; - Initialize(substitution); + AddSubstitution(substitution); + this.fieldDefinition = (IField)base.MemberDefinition; } public bool IsReadOnly { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs index 6d1e04b0e6..b616783ff4 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; @@ -31,58 +32,115 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// public abstract class SpecializedMember : IMember { - readonly IType declaringType; readonly IMember memberDefinition; + TypeParameterSubstitution substitution; + + IType declaringType; IType returnType; - protected SpecializedMember(IType declaringType, IMember memberDefinition) + protected SpecializedMember(IMember memberDefinition) { - if (declaringType == null) - throw new ArgumentNullException("declaringType"); if (memberDefinition == null) throw new ArgumentNullException("memberDefinition"); - this.declaringType = declaringType; - this.memberDefinition = memberDefinition; + SpecializedMember sm = memberDefinition as SpecializedMember; + if (sm != null) { + this.memberDefinition = sm.memberDefinition; + this.substitution = sm.substitution; + } else { + this.memberDefinition = memberDefinition; + this.substitution = TypeParameterSubstitution.Identity; + } } - protected virtual void Initialize(TypeVisitor substitution) + /// + /// Performs a substitution. This method may only be called by constructors in derived classes. + /// + protected void AddSubstitution(TypeParameterSubstitution newSubstitution) { - this.returnType = Substitute(memberDefinition.ReturnType, substitution); + Debug.Assert(declaringType == null); + Debug.Assert(returnType == null); + this.substitution = TypeParameterSubstitution.Compose(newSubstitution, this.substitution); } - public virtual IMemberReference ToMemberReference() + public static SpecializedMember Create(IMember memberDefinition, TypeParameterSubstitution substitution) { - return new SpecializingMemberReference(declaringType.ToTypeReference(), memberDefinition.ToMemberReference()); + if (memberDefinition == null) { + return null; + } else if (memberDefinition is IMethod) { + return new SpecializedMethod((IMethod)memberDefinition, substitution); + } else if (memberDefinition is IProperty) { + return new SpecializedProperty((IProperty)memberDefinition, substitution); + } else if (memberDefinition is IField) { + return new SpecializedField((IField)memberDefinition, substitution); + } else if (memberDefinition is IEvent) { + return new SpecializedEvent((IEvent)memberDefinition, substitution); + } else { + throw new NotSupportedException("Unknown IMember: " + memberDefinition); + } } - internal static TypeVisitor GetSubstitution(IType declaringType) + public IMemberReference ToMemberReference() { - ParameterizedType pt = declaringType as ParameterizedType; - if (pt != null) - return pt.GetSubstitution(); - else - return null; + return new SpecializingMemberReference( + memberDefinition.ToMemberReference(), + ToTypeReference(substitution.ClassTypeArguments), + ToTypeReference(substitution.MethodTypeArguments)); } - internal static IType Substitute(IType type, TypeVisitor substitution) + static IList ToTypeReference(IList typeArguments) { - if (substitution == null) - return type; + if (typeArguments == null) + return null; else - return type.AcceptVisitor(substitution); + return typeArguments.Select(t => t.ToTypeReference()).ToArray(); } - internal IMethod WrapAccessor(IMethod accessorDefinition) + internal IMethod WrapAccessor(ref IMethod cachingField, IMethod accessorDefinition) { if (accessorDefinition == null) return null; + var result = LazyInit.VolatileRead(ref cachingField); + if (result != null) + return result; else - return new SpecializedMethod(declaringType, accessorDefinition); + return LazyInit.GetOrSet(ref cachingField, new SpecializedMethod(accessorDefinition, substitution)); + } + + /// + /// Gets the substitution belonging to this specialized member. + /// + public TypeParameterSubstitution Substitution { + get { return substitution; } } public IType DeclaringType { - get { return declaringType; } + get { + var result = LazyInit.VolatileRead(ref this.declaringType); + if (result != null) + return result; + IType definitionDeclaringType = memberDefinition.DeclaringType; + ITypeDefinition definitionDeclaringTypeDef = definitionDeclaringType as ITypeDefinition; + if (definitionDeclaringTypeDef != null && definitionDeclaringType.TypeParameterCount > 0) { + if (substitution.ClassTypeArguments != null && substitution.ClassTypeArguments.Count == definitionDeclaringType.TypeParameterCount) { + result = new ParameterizedType(definitionDeclaringTypeDef, substitution.ClassTypeArguments); + } else { + result = new ParameterizedType(definitionDeclaringTypeDef, definitionDeclaringTypeDef.TypeParameters).AcceptVisitor(substitution); + } + } else { + result = definitionDeclaringType.AcceptVisitor(substitution); + } + return LazyInit.GetOrSet(ref this.declaringType, result); + } + internal set { + // This setter is used as an optimization when the code constructing + // the SpecializedMember already knows the declaring type. + Debug.Assert(this.declaringType == null); + Debug.Assert(value != null); + // As this setter is used only during construction before the member is published + // to other threads, we don't need a volatile write. + this.declaringType = value; + } } public IMember MemberDefinition { @@ -94,8 +152,21 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } public IType ReturnType { - get { return returnType; } - protected set { returnType = value; } + get { + var result = LazyInit.VolatileRead(ref this.returnType); + if (result != null) + return result; + else + return LazyInit.GetOrSet(ref this.returnType, memberDefinition.ReturnType.AcceptVisitor(substitution)); + } + protected set { + // This setter is used for LiftedUserDefinedOperator, a special case of specialized member + // (not a normal type parameter substitution). + + // As this setter is used only during construction before the member is published + // to other threads, we don't need a volatile write. + this.returnType = value; + } } public bool IsVirtual { @@ -143,19 +214,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation var definitionImplementations = memberDefinition.ImplementedInterfaceMembers; IMember[] result = new IMember[definitionImplementations.Count]; for (int i = 0; i < result.Length; i++) { - result[i] = Specialize(definitionImplementations[i]); + result[i] = SpecializedMember.Create(definitionImplementations[i], substitution); } return result; } - /// - /// Specialize another member using the same type arguments as this member. - /// - protected virtual IMember Specialize(IMember otherMember) - { - return SpecializingMemberReference.CreateSpecializedMember(declaringType, memberDefinition, null); - } - public bool IsExplicitInterfaceImplementation { get { return memberDefinition.IsExplicitInterfaceImplementation; } } @@ -241,13 +304,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation SpecializedMember other = obj as SpecializedMember; if (other == null) return false; - return this.declaringType.Equals(other.declaringType) && this.memberDefinition.Equals(other.memberDefinition); + return this.memberDefinition.Equals(other.memberDefinition) && this.substitution.Equals(other.substitution); } public override int GetHashCode() { unchecked { - return 1000000007 * declaringType.GetHashCode() + 1000000009 * memberDefinition.GetHashCode(); + return 1000000007 * memberDefinition.GetHashCode() + 1000000009 * substitution.GetHashCode(); } } @@ -256,11 +319,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation StringBuilder b = new StringBuilder("["); b.Append(GetType().Name); b.Append(' '); - b.Append(declaringType.ToString()); + b.Append(this.DeclaringType.ToString()); b.Append('.'); b.Append(this.Name); b.Append(':'); - b.Append(returnType.ToString()); + b.Append(this.ReturnType.ToString()); b.Append(']'); return b.ToString(); } @@ -270,36 +333,48 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { IList parameters; - protected SpecializedParameterizedMember(IType declaringType, IParameterizedMember memberDefinition) - : base(declaringType, memberDefinition) + protected SpecializedParameterizedMember(IParameterizedMember memberDefinition) + : base(memberDefinition) { } - protected override void Initialize(TypeVisitor substitution) + public IList Parameters { + get { + var result = LazyInit.VolatileRead(ref this.parameters); + if (result != null) + return result; + else + return LazyInit.GetOrSet(ref this.parameters, CreateParameters(this.Substitution)); + } + protected set { + // This setter is used for LiftedUserDefinedOperator, a special case of specialized member + // (not a normal type parameter substitution). + + // As this setter is used only during construction before the member is published + // to other threads, we don't need a volatile write. + this.parameters = value; + } + } + + protected IList CreateParameters(TypeVisitor substitution) { - base.Initialize(substitution); - var paramDefs = ((IParameterizedMember)this.MemberDefinition).Parameters; if (paramDefs.Count == 0) { - this.parameters = EmptyList.Instance; + return EmptyList.Instance; } else { var parameters = new IParameter[paramDefs.Count]; for (int i = 0; i < parameters.Length; i++) { - IType newType = Substitute(paramDefs[i].Type, substitution); + IType newType = paramDefs[i].Type.AcceptVisitor(substitution); if (newType != paramDefs[i].Type) { parameters[i] = new SpecializedParameter(paramDefs[i], newType); } else { parameters[i] = paramDefs[i]; } } - this.parameters = Array.AsReadOnly(parameters); + return Array.AsReadOnly(parameters); } } - public IList Parameters { - get { return parameters; } - } - public override string ToString() { StringBuilder b = new StringBuilder("["); @@ -309,9 +384,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append('.'); b.Append(this.Name); b.Append('('); - for (int i = 0; i < parameters.Count; i++) { + for (int i = 0; i < this.Parameters.Count; i++) { if (i > 0) b.Append(", "); - b.Append(parameters[i].ToString()); + b.Append(this.Parameters[i].ToString()); } b.Append("):"); b.Append(this.ReturnType.ToString()); @@ -326,7 +401,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public SpecializedParameter(IParameter originalParameter, IType newType) { - this.originalParameter = originalParameter; + if (originalParameter is SpecializedParameter) + this.originalParameter = ((SpecializedParameter)originalParameter).originalParameter; + else + this.originalParameter = originalParameter; this.newType = newType; } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs index 18584147fd..3e8a0e920d 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs @@ -32,50 +32,36 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public class SpecializedMethod : SpecializedParameterizedMember, IMethod { readonly IMethod methodDefinition; - readonly IList typeArguments; - readonly IList specializedTypeParameters; + readonly ITypeParameter[] specializedTypeParameters; - public SpecializedMethod(IType declaringType, IMethod methodDefinition, IList typeArguments = null) - : this(declaringType, methodDefinition, typeArguments, GetSubstitution(declaringType, typeArguments)) + public SpecializedMethod(IMethod methodDefinition, TypeParameterSubstitution substitution) + : base(methodDefinition) { - } - - internal protected SpecializedMethod(IType declaringType, IMethod methodDefinition, IList typeArguments, TypeVisitor substitution) - : base(declaringType, methodDefinition) - { - if (declaringType == null) - throw new ArgumentNullException("declaringType"); - if (methodDefinition == null) - throw new ArgumentNullException("methodDefinition"); - + // The base ctor might have unpacked a SpecializedMember + // (in case we are specializing an already-specialized method) + methodDefinition = (IMethod)base.MemberDefinition; this.methodDefinition = methodDefinition; - this.typeArguments = typeArguments ?? EmptyList.Instance; - if (methodDefinition.TypeParameters.Any(ConstraintNeedsSpecialization)) { // The method is generic, and we need to specialize the type parameters specializedTypeParameters = new ITypeParameter[methodDefinition.TypeParameters.Count]; - for (int i = 0; i < specializedTypeParameters.Count; i++) { + for (int i = 0; i < specializedTypeParameters.Length; i++) { ITypeParameter tp = methodDefinition.TypeParameters[i]; if (ConstraintNeedsSpecialization(tp)) - tp = new SpecializedTypeParameter(tp, this, substitution); + tp = new SpecializedTypeParameter(tp, this); specializedTypeParameters[i] = tp; } + // add substitution that replaces the base method's type parameters with our specialized version + AddSubstitution(new TypeParameterSubstitution(null, specializedTypeParameters)); } - if (typeArguments != null && typeArguments.Count > 0) { - if (typeArguments.Count != methodDefinition.TypeParameters.Count) - throw new ArgumentException("Incorrect number of type arguments"); - } else if (specializedTypeParameters != null) { - // No type arguments were specified, but the method is generic. - // -> substitute original type parameters with the specialized ones - substitution = GetSubstitution(declaringType, specializedTypeParameters.ToArray()); - for (int i = 0; i < specializedTypeParameters.Count; i++) { - if (ConstraintNeedsSpecialization(methodDefinition.TypeParameters[i])) { - ((SpecializedTypeParameter)specializedTypeParameters[i]).substitution = substitution; - } + // Add the main substitution after the method type parameter specialization. + AddSubstitution(substitution); + if (specializedTypeParameters != null) { + // Set the substitution on the type parameters to the final composed substitution + foreach (var tp in specializedTypeParameters.OfType()) { + if (tp.Owner == this) + tp.substitution = base.Substitution; } } - - Initialize(substitution); } static bool ConstraintNeedsSpecialization(ITypeParameter tp) @@ -95,53 +81,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return null; } - public override IMemberReference ToMemberReference() - { - return new SpecializingMemberReference( - this.DeclaringType.ToTypeReference(), - this.MemberDefinition.ToMemberReference(), - typeArguments.Select(ta => ta.ToTypeReference()).ToList() - ); - } - - protected override IMember Specialize(IMember otherMember) - { - return SpecializingMemberReference.CreateSpecializedMember(this.DeclaringType, this.MemberDefinition, typeArguments); - } - /// /// Gets the type arguments passed to this method. /// If only the type parameters for the class were specified and the generic method /// itself is not specialized yet, this property will return an empty list. /// public IList TypeArguments { - get { return typeArguments; } - } - - public override int GetHashCode() - { - int hashCode = base.GetHashCode(); - unchecked { - for (int i = 0; i < typeArguments.Count; i++) { - hashCode *= 362631391; - hashCode += typeArguments[i].GetHashCode(); - } - } - return hashCode; - } - - public override bool Equals(object obj) - { - SpecializedMethod other = obj as SpecializedMethod; - if (!base.Equals(other)) - return false; - if (typeArguments.Count != other.typeArguments.Count) - return false; - for (int i = 0; i < typeArguments.Count; i++) { - if (!typeArguments[i].Equals(other.typeArguments[i])) - return false; - } - return true; + get { return this.Substitution.MethodTypeArguments ?? EmptyList.Instance; } } public IList Parts { @@ -182,11 +128,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation b.Append(this.DeclaringType.ToString()); b.Append('.'); b.Append(this.Name); - if (typeArguments.Count > 0) { + if (this.TypeArguments.Count > 0) { b.Append('['); - for (int i = 0; i < typeArguments.Count; i++) { + for (int i = 0; i < this.TypeArguments.Count; i++) { if (i > 0) b.Append(", "); - b.Append(typeArguments[i].ToString()); + b.Append(this.TypeArguments[i].ToString()); } b.Append(']'); } @@ -205,14 +151,15 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { readonly ITypeParameter baseTp; - // not readonly: The substition may be replaced at the end of SpecializedMethod constructor + // The substition is set at the end of SpecializedMethod constructor internal TypeVisitor substitution; - public SpecializedTypeParameter(ITypeParameter baseTp, IMethod specializedOwner, TypeVisitor substitution) + public SpecializedTypeParameter(ITypeParameter baseTp, IMethod specializedOwner) : base(specializedOwner, baseTp.Index, baseTp.Name, baseTp.Variance, baseTp.Attributes, baseTp.Region) { + // We don't have to consider already-specialized baseTps because + // we read the baseTp directly from the unpacked memberDefinition. this.baseTp = baseTp; - this.substitution = substitution; } public override int GetHashCode() diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs index f3a63ada23..c35c9e1a68 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedProperty.cs @@ -27,18 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { readonly IProperty propertyDefinition; - public SpecializedProperty(IType declaringType, IProperty propertyDefinition) - : base(declaringType, propertyDefinition) + public SpecializedProperty(IProperty propertyDefinition, TypeParameterSubstitution substitution) + : base(propertyDefinition) { - this.propertyDefinition = propertyDefinition; - Initialize(GetSubstitution(declaringType)); - } - - internal SpecializedProperty(IType declaringType, IProperty propertyDefinition, TypeVisitor substitution) - : base(declaringType, propertyDefinition) - { - this.propertyDefinition = propertyDefinition; - Initialize(substitution); + AddSubstitution(substitution); + this.propertyDefinition = (IProperty)base.MemberDefinition; } public bool CanGet { @@ -49,12 +42,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return propertyDefinition.CanSet; } } + IMethod getter, setter; + public IMethod Getter { - get { return WrapAccessor(propertyDefinition.Getter); } + get { return WrapAccessor(ref this.getter, propertyDefinition.Getter); } } public IMethod Setter { - get { return WrapAccessor(propertyDefinition.Setter); } + get { return WrapAccessor(ref this.setter, propertyDefinition.Setter); } } public bool IsIndexer { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs index 6faecc92a8..d560fa4f85 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializingMemberReference.cs @@ -24,50 +24,29 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation [Serializable] public sealed class SpecializingMemberReference : IMemberReference { - ITypeReference declaringTypeReference; IMemberReference memberDefinitionReference; - IList typeArgumentReferences; + IList classTypeArgumentReferences; + IList methodTypeArgumentReferences; - public SpecializingMemberReference(ITypeReference declaringTypeReference, IMemberReference memberDefinitionReference, IList typeArgumentReferences = null) + public SpecializingMemberReference(IMemberReference memberDefinitionReference, IList classTypeArgumentReferences = null, IList methodTypeArgumentReferences = null) { - if (declaringTypeReference == null) - throw new ArgumentNullException("declaringTypeReference"); if (memberDefinitionReference == null) throw new ArgumentNullException("memberDefinitionReference"); - this.declaringTypeReference = declaringTypeReference; this.memberDefinitionReference = memberDefinitionReference; - this.typeArgumentReferences = typeArgumentReferences; + this.classTypeArgumentReferences = classTypeArgumentReferences; + this.methodTypeArgumentReferences = methodTypeArgumentReferences; } public IMember Resolve(ITypeResolveContext context) { - var declaringType = declaringTypeReference.Resolve(context); var memberDefinition = memberDefinitionReference.Resolve(context); - IType[] typeArguments = null; - if (typeArgumentReferences != null) { - typeArguments = new IType[typeArgumentReferences.Count]; - for (int i = 0; i < typeArguments.Length; i++) { - typeArguments[i] = typeArgumentReferences[i].Resolve(context); - } - } - return CreateSpecializedMember(declaringType, memberDefinition, typeArguments); - } - - internal static IMember CreateSpecializedMember(IType declaringType, IMember memberDefinition, IList typeArguments) - { - if (memberDefinition == null) { - return null; - } else if (memberDefinition is IMethod) { - return new SpecializedMethod(declaringType, (IMethod)memberDefinition, typeArguments); - } else if (memberDefinition is IProperty) { - return new SpecializedProperty(declaringType, (IProperty)memberDefinition); - } else if (memberDefinition is IField) { - return new SpecializedField(declaringType, (IField)memberDefinition); - } else if (memberDefinition is IEvent) { - return new SpecializedEvent(declaringType, (IEvent)memberDefinition); - } else { - throw new NotSupportedException("Unknown IMember: " + memberDefinition); - } + return SpecializedMember.Create( + memberDefinition, + new TypeParameterSubstitution( + classTypeArgumentReferences != null ? classTypeArgumentReferences.Resolve(context) : null, + methodTypeArgumentReferences != null ? methodTypeArgumentReferences.Resolve(context) : null + ) + ); } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs index 925e9e2d64..6571c6f3d6 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/TypeParameterSubstitution.cs @@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// public class TypeParameterSubstitution : TypeVisitor { + /// + /// The identity function. + /// + public static readonly TypeParameterSubstitution Identity = new TypeParameterSubstitution(null, null); + readonly IList classTypeArguments; readonly IList methodTypeArguments; @@ -47,6 +52,95 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation this.methodTypeArguments = methodTypeArguments; } + /// + /// Gets the list of class type arguments. + /// Returns null if this substitution keeps class type parameter unmodified. + /// + public IList ClassTypeArguments { + get { return classTypeArguments; } + } + + public IList MethodTypeArguments { + get { return methodTypeArguments; } + } + + #region Compose + /// + /// Computes a single TypeParameterSubstitution so that for all types t: + /// t.AcceptVisitor(Compose(g, f)) equals t.AcceptVisitor(f).AcceptVisitor(g) + /// + /// If you consider type parameter substitution to be a function, this is function composition. + public static TypeParameterSubstitution Compose(TypeParameterSubstitution g, TypeParameterSubstitution f) + { + if (g == null) + return f; + if (f == null || (f.classTypeArguments == null && f.methodTypeArguments == null)) + return g; + // The composition is a copy of 'f', with 'g' applied on the array elements. + // If 'f' has a null list (keeps type parameters unmodified), we have to treat it as + // the identity function, and thus use the list from 'g'. + var classTypeArguments = f.classTypeArguments != null ? GetComposedTypeArguments(f.classTypeArguments, g) : g.classTypeArguments; + var methodTypeArguments = f.methodTypeArguments != null ? GetComposedTypeArguments(f.methodTypeArguments, g) : g.methodTypeArguments; + return new TypeParameterSubstitution(classTypeArguments, methodTypeArguments); + } + + static IList GetComposedTypeArguments(IList input, TypeParameterSubstitution substitution) + { + IType[] result = new IType[input.Count]; + for (int i = 0; i < result.Length; i++) { + result[i] = input[i].AcceptVisitor(substitution); + } + return result; + } + #endregion + + #region Equals and GetHashCode implementation + public override bool Equals(object obj) + { + TypeParameterSubstitution other = obj as TypeParameterSubstitution; + if (other == null) + return false; + return TypeListEquals(classTypeArguments, other.classTypeArguments) + && TypeListEquals(methodTypeArguments, other.methodTypeArguments); + } + + public override int GetHashCode() + { + unchecked { + return 1124131 * TypeListHashCode(classTypeArguments) + 1821779 * TypeListHashCode(methodTypeArguments); + } + } + + static bool TypeListEquals(IList a, IList b) + { + if (a == b) + return true; + if (a == null || b == null) + return false; + if (a.Count != b.Count) + return false; + for (int i = 0; i < a.Count; i++) { + if (!a[i].Equals(b[i])) + return false; + } + return true; + } + + static int TypeListHashCode(IList obj) + { + if (obj == null) + return 0; + unchecked { + int hashCode = 1; + foreach (var element in obj) { + hashCode *= 27; + hashCode += element.GetHashCode(); + } + return hashCode; + } + } + #endregion + public override IType VisitTypeParameter(ITypeParameter type) { int index = type.Index;