// // generic.cs: Generics support // // Authors: Martin Baulig (martin@ximian.com) // Miguel de Icaza (miguel@ximian.com) // Marek Safar (marek.safar@gmail.com) // // Dual licensed under the terms of the MIT X11 or GNU GPL // // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) // Copyright 2004-2008 Novell, Inc // Copyright 2011 Xamarin, Inc (http://www.xamarin.com) // using System; using System.Collections.Generic; using System.Text; using System.Linq; #if STATIC using MetaType = IKVM.Reflection.Type; using IKVM.Reflection; using IKVM.Reflection.Emit; #else using MetaType = System.Type; using System.Reflection; using System.Reflection.Emit; #endif namespace Mono.CSharp { public class VarianceDecl { public VarianceDecl (Variance variance, Location loc) { this.Variance = variance; this.Location = loc; } public Variance Variance { get; private set; } public Location Location { get; private set; } public static Variance CheckTypeVariance (TypeSpec t, Variance expected, IMemberContext member) { var tp = t as TypeParameterSpec; if (tp != null) { var v = tp.Variance; if (expected == Variance.None && v != expected || expected == Variance.Covariant && v == Variance.Contravariant || expected == Variance.Contravariant && v == Variance.Covariant) { ((TypeParameter) tp.MemberDefinition).ErrorInvalidVariance (member, expected); } return expected; } if (t.TypeArguments.Length > 0) { var targs_definition = t.MemberDefinition.TypeParameters; TypeSpec[] targs = TypeManager.GetTypeArguments (t); for (int i = 0; i < targs.Length; ++i) { var v = targs_definition[i].Variance; CheckTypeVariance (targs[i], (Variance) ((int) v * (int) expected), member); } return expected; } var ac = t as ArrayContainer; if (ac != null) return CheckTypeVariance (ac.Element, expected, member); return Variance.None; } } public enum Variance { // // Don't add or modify internal values, they are used as -/+ calculation signs // None = 0, Covariant = 1, Contravariant = -1 } [Flags] public enum SpecialConstraint { None = 0, Constructor = 1 << 2, Class = 1 << 3, Struct = 1 << 4 } public class SpecialContraintExpr : FullNamedExpression { public SpecialContraintExpr (SpecialConstraint constraint, Location loc) { this.loc = loc; this.Constraint = constraint; } public SpecialConstraint Constraint { get; private set; } protected override Expression DoResolve (ResolveContext rc) { throw new NotImplementedException (); } public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc, bool allowUnboundTypeArguments) { throw new NotImplementedException (); } } // // A set of parsed constraints for a type parameter // public class Constraints { readonly SimpleMemberName tparam; readonly List constraints; readonly Location loc; bool resolved; bool resolving; public Constraints (SimpleMemberName tparam, List constraints, Location loc) { this.tparam = tparam; this.constraints = constraints; this.loc = loc; } #region Properties public List TypeExpressions { get { return constraints; } } public Location Location { get { return loc; } } public SimpleMemberName TypeParameter { get { return tparam; } } #endregion public static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec bb, IMemberContext context, Location loc) { if (spec.HasSpecialClass && bb.IsStruct) { context.Module.Compiler.Report.Error (455, loc, "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'", spec.Name, "class", bb.GetSignatureForError ()); return false; } return CheckConflictingInheritedConstraint (spec, spec.BaseType, bb, context, loc); } static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec ba, TypeSpec bb, IMemberContext context, Location loc) { if (ba == bb) return true; if (TypeSpec.IsBaseClass (ba, bb, false) || TypeSpec.IsBaseClass (bb, ba, false)) return true; Error_ConflictingConstraints (context, spec, ba, bb, loc); return false; } public static void Error_ConflictingConstraints (IMemberContext context, TypeParameterSpec tp, TypeSpec ba, TypeSpec bb, Location loc) { context.Module.Compiler.Report.Error (455, loc, "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'", tp.Name, ba.GetSignatureForError (), bb.GetSignatureForError ()); } public void CheckGenericConstraints (IMemberContext context, bool obsoleteCheck) { foreach (var c in constraints) { if (c == null) continue; var t = c.Type; if (t == null) continue; if (obsoleteCheck) { t.CheckObsoleteness (context, c.Location); } ConstraintChecker.Check (context, t, c.Location); } } // // Resolve the constraints types with only possible early checks, return // value `false' is reserved for recursive failure // public bool Resolve (IMemberContext context, TypeParameter tp) { if (resolved) return true; if (resolving) return false; resolving = true; var spec = tp.Type; List tparam_types = null; bool iface_found = false; spec.BaseType = context.Module.Compiler.BuiltinTypes.Object; for (int i = 0; i < constraints.Count; ++i) { var constraint = constraints[i]; if (constraint is SpecialContraintExpr) { spec.SpecialConstraint |= ((SpecialContraintExpr) constraint).Constraint; if (spec.HasSpecialStruct) spec.BaseType = context.Module.Compiler.BuiltinTypes.ValueType; // Set to null as it does not have a type constraints[i] = null; continue; } var type = constraint.ResolveAsType (context); if (type == null) continue; if (type.Arity > 0 && ((InflatedTypeSpec) type).HasDynamicArgument ()) { context.Module.Compiler.Report.Error (1968, constraint.Location, "A constraint cannot be the dynamic type `{0}'", type.GetSignatureForError ()); continue; } if (!context.CurrentMemberDefinition.IsAccessibleAs (type)) { context.Module.Compiler.Report.SymbolRelatedToPreviousError (type); context.Module.Compiler.Report.Error (703, loc, "Inconsistent accessibility: constraint type `{0}' is less accessible than `{1}'", type.GetSignatureForError (), context.GetSignatureForError ()); } if (type.IsInterface) { if (!spec.AddInterface (type)) { context.Module.Compiler.Report.Error (405, constraint.Location, "Duplicate constraint `{0}' for type parameter `{1}'", type.GetSignatureForError (), tparam.Value); } iface_found = true; continue; } var constraint_tp = type as TypeParameterSpec; if (constraint_tp != null) { if (tparam_types == null) { tparam_types = new List (2); } else if (tparam_types.Contains (constraint_tp)) { context.Module.Compiler.Report.Error (405, constraint.Location, "Duplicate constraint `{0}' for type parameter `{1}'", type.GetSignatureForError (), tparam.Value); continue; } // // Checks whether each generic method parameter constraint type // is valid with respect to T // if (tp.IsMethodTypeParameter) { VarianceDecl.CheckTypeVariance (type, Variance.Contravariant, context); } var tp_def = constraint_tp.MemberDefinition as TypeParameter; if (tp_def != null && !tp_def.ResolveConstraints (context)) { context.Module.Compiler.Report.Error (454, constraint.Location, "Circular constraint dependency involving `{0}' and `{1}'", constraint_tp.GetSignatureForError (), tp.GetSignatureForError ()); continue; } // // Checks whether there are no conflicts between type parameter constraints // // class Foo // where T : A // where U : B, T // // A and B are not convertible and only 1 class constraint is allowed // if (constraint_tp.HasTypeConstraint) { if (spec.HasTypeConstraint || spec.HasSpecialStruct) { if (!CheckConflictingInheritedConstraint (spec, constraint_tp.BaseType, context, constraint.Location)) continue; } else { for (int ii = 0; ii < tparam_types.Count; ++ii) { if (!tparam_types[ii].HasTypeConstraint) continue; if (!CheckConflictingInheritedConstraint (spec, tparam_types[ii].BaseType, constraint_tp.BaseType, context, constraint.Location)) break; } } } if (constraint_tp.TypeArguments != null) { var eb = constraint_tp.GetEffectiveBase (); if (eb != null && !CheckConflictingInheritedConstraint (spec, eb, spec.BaseType, context, constraint.Location)) break; } if (constraint_tp.HasSpecialStruct) { context.Module.Compiler.Report.Error (456, constraint.Location, "Type parameter `{0}' has the `struct' constraint, so it cannot be used as a constraint for `{1}'", constraint_tp.GetSignatureForError (), tp.GetSignatureForError ()); continue; } tparam_types.Add (constraint_tp); continue; } if (iface_found || spec.HasTypeConstraint) { context.Module.Compiler.Report.Error (406, constraint.Location, "The class type constraint `{0}' must be listed before any other constraints. Consider moving type constraint to the beginning of the constraint list", type.GetSignatureForError ()); } if (spec.HasSpecialStruct || spec.HasSpecialClass) { context.Module.Compiler.Report.Error (450, constraint.Location, "`{0}': cannot specify both a constraint class and the `class' or `struct' constraint", type.GetSignatureForError ()); } switch (type.BuiltinType) { case BuiltinTypeSpec.Type.Array: case BuiltinTypeSpec.Type.Delegate: case BuiltinTypeSpec.Type.MulticastDelegate: case BuiltinTypeSpec.Type.Enum: case BuiltinTypeSpec.Type.ValueType: case BuiltinTypeSpec.Type.Object: context.Module.Compiler.Report.Error (702, constraint.Location, "A constraint cannot be special class `{0}'", type.GetSignatureForError ()); continue; case BuiltinTypeSpec.Type.Dynamic: context.Module.Compiler.Report.Error (1967, constraint.Location, "A constraint cannot be the dynamic type"); continue; } if (type.IsSealed || !type.IsClass) { context.Module.Compiler.Report.Error (701, loc, "`{0}' is not a valid constraint. A constraint must be an interface, a non-sealed class or a type parameter", type.GetSignatureForError ()); continue; } if (type.IsStatic) { context.Module.Compiler.Report.Error (717, constraint.Location, "`{0}' is not a valid constraint. Static classes cannot be used as constraints", type.GetSignatureForError ()); } spec.BaseType = type; } if (tparam_types != null) spec.TypeArguments = tparam_types.ToArray (); resolving = false; resolved = true; return true; } public void VerifyClsCompliance (Report report) { foreach (var c in constraints) { if (c == null) continue; if (!c.Type.IsCLSCompliant ()) { report.SymbolRelatedToPreviousError (c.Type); report.Warning (3024, 1, loc, "Constraint type `{0}' is not CLS-compliant", c.Type.GetSignatureForError ()); } } } } // // A type parameter for a generic type or generic method definition // public class TypeParameter : MemberCore, ITypeDefinition { static readonly string[] attribute_target = { "type parameter" }; Constraints constraints; GenericTypeParameterBuilder builder; readonly TypeParameterSpec spec; public TypeParameter (int index, MemberName name, Constraints constraints, Attributes attrs, Variance Variance) : base (null, name, attrs) { this.constraints = constraints; this.spec = new TypeParameterSpec (null, index, this, SpecialConstraint.None, Variance, null); } // // Used by parser // public TypeParameter (MemberName name, Attributes attrs, VarianceDecl variance) : base (null, name, attrs) { var var = variance == null ? Variance.None : variance.Variance; this.spec = new TypeParameterSpec (null, -1, this, SpecialConstraint.None, var, null); this.VarianceDecl = variance; } public TypeParameter (TypeParameterSpec spec, TypeSpec parentSpec, MemberName name, Attributes attrs) : base (null, name, attrs) { this.spec = new TypeParameterSpec (parentSpec, spec.DeclaredPosition, spec.MemberDefinition, spec.SpecialConstraint, spec.Variance, null) { BaseType = spec.BaseType, InterfacesDefined = spec.InterfacesDefined, TypeArguments = spec.TypeArguments }; } #region Properties public override AttributeTargets AttributeTargets { get { return AttributeTargets.GenericParameter; } } public Constraints Constraints { get { return constraints; } set { constraints = value; } } public IAssemblyDefinition DeclaringAssembly { get { return Module.DeclaringAssembly; } } public override string DocCommentHeader { get { throw new InvalidOperationException ( "Unexpected attempt to get doc comment from " + this.GetType ()); } } bool ITypeDefinition.IsComImport { get { return false; } } bool ITypeDefinition.IsPartial { get { return false; } } public bool IsMethodTypeParameter { get { return spec.IsMethodOwned; } } bool ITypeDefinition.IsTypeForwarder { get { return false; } } bool ITypeDefinition.IsCyclicTypeForwarder { get { return false; } } public string Name { get { return MemberName.Name; } } public string Namespace { get { return null; } } public TypeParameterSpec Type { get { return spec; } } public int TypeParametersCount { get { return 0; } } public TypeParameterSpec[] TypeParameters { get { return null; } } public override string[] ValidAttributeTargets { get { return attribute_target; } } public Variance Variance { get { return spec.Variance; } } public VarianceDecl VarianceDecl { get; private set; } #endregion // // This is called for each part of a partial generic type definition. // // If partial type parameters constraints are not null and we don't // already have constraints they become our constraints. If we already // have constraints, we must check that they're same. // public bool AddPartialConstraints (TypeDefinition part, TypeParameter tp) { if (builder == null) throw new InvalidOperationException (); var new_constraints = tp.constraints; if (new_constraints == null) return true; // TODO: could create spec only //tp.Define (null, -1, part.Definition); tp.spec.DeclaringType = part.Definition; if (!tp.ResolveConstraints (part)) return false; if (constraints != null) return spec.HasSameConstraintsDefinition (tp.Type); // Copy constraint from resolved part to partial container spec.SpecialConstraint = tp.spec.SpecialConstraint; spec.InterfacesDefined = tp.spec.InterfacesDefined; spec.TypeArguments = tp.spec.TypeArguments; spec.BaseType = tp.spec.BaseType; return true; } public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) { builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata); } public void CheckGenericConstraints (bool obsoleteCheck) { if (constraints != null) constraints.CheckGenericConstraints (this, obsoleteCheck); } public TypeParameter CreateHoistedCopy (TypeSpec declaringSpec) { return new TypeParameter (spec, declaringSpec, MemberName, null); } public override bool Define () { return true; } // // This is the first method which is called during the resolving // process; we're called immediately after creating the type parameters // with SRE (by calling `DefineGenericParameters()' on the TypeBuilder / // MethodBuilder). // public void Create (TypeSpec declaringType, TypeContainer parent) { if (builder != null) throw new InternalErrorException (); // Needed to get compiler reference this.Parent = parent; spec.DeclaringType = declaringType; } public void Define (GenericTypeParameterBuilder type) { this.builder = type; spec.SetMetaInfo (type); } public void Define (TypeParameter tp) { builder = tp.builder; } public void EmitConstraints (GenericTypeParameterBuilder builder) { var attr = GenericParameterAttributes.None; if (spec.Variance == Variance.Contravariant) attr |= GenericParameterAttributes.Contravariant; else if (spec.Variance == Variance.Covariant) attr |= GenericParameterAttributes.Covariant; if (spec.HasSpecialClass) attr |= GenericParameterAttributes.ReferenceTypeConstraint; else if (spec.HasSpecialStruct) attr |= GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint; if (spec.HasSpecialConstructor) attr |= GenericParameterAttributes.DefaultConstructorConstraint; if (spec.BaseType.BuiltinType != BuiltinTypeSpec.Type.Object) builder.SetBaseTypeConstraint (spec.BaseType.GetMetaInfo ()); if (spec.InterfacesDefined != null) builder.SetInterfaceConstraints (spec.InterfacesDefined.Select (l => l.GetMetaInfo ()).ToArray ()); if (spec.TypeArguments != null) { var meta_constraints = new List (spec.TypeArguments.Length); foreach (var c in spec.TypeArguments) { // // Inflated type parameters can collide with base type constraint, don't // emit any such type parameter. // if (c.IsClass && spec.BaseType.BuiltinType != BuiltinTypeSpec.Type.Object) continue; meta_constraints.Add (c.GetMetaInfo ()); } builder.SetInterfaceConstraints (meta_constraints.ToArray ()); } builder.SetGenericParameterAttributes (attr); } public override void Emit () { EmitConstraints (builder); if (OptAttributes != null) OptAttributes.Emit (); base.Emit (); } public void ErrorInvalidVariance (IMemberContext mc, Variance expected) { Report.SymbolRelatedToPreviousError (mc.CurrentMemberDefinition); string input_variance = Variance == Variance.Contravariant ? "contravariant" : "covariant"; string gtype_variance; switch (expected) { case Variance.Contravariant: gtype_variance = "contravariantly"; break; case Variance.Covariant: gtype_variance = "covariantly"; break; default: gtype_variance = "invariantly"; break; } Delegate d = mc as Delegate; string parameters = d != null ? d.Parameters.GetSignatureForError () : ""; Report.Error (1961, Location, "The {2} type parameter `{0}' must be {3} valid on `{1}{4}'", GetSignatureForError (), mc.GetSignatureForError (), input_variance, gtype_variance, parameters); } public TypeSpec GetAttributeCoClass () { return null; } public string GetAttributeDefaultMember () { throw new NotSupportedException (); } public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa) { throw new NotSupportedException (); } public override string GetSignatureForDocumentation () { throw new NotImplementedException (); } public override string GetSignatureForError () { return MemberName.Name; } bool ITypeDefinition.IsInternalAsPublic (IAssemblyDefinition assembly) { return spec.MemberDefinition.DeclaringAssembly == assembly; } public void LoadMembers (TypeSpec declaringType, bool onlyTypes, ref MemberCache cache) { throw new NotSupportedException ("Not supported for compiled definition"); } // // Resolves all type parameter constraints // public bool ResolveConstraints (IMemberContext context) { if (constraints != null) return constraints.Resolve (context, this); if (spec.BaseType == null) spec.BaseType = context.Module.Compiler.BuiltinTypes.Object; return true; } public override bool IsClsComplianceRequired () { return false; } public new void VerifyClsCompliance () { if (constraints != null) constraints.VerifyClsCompliance (Report); } public void WarningParentNameConflict (TypeParameter conflict) { conflict.Report.SymbolRelatedToPreviousError (conflict.Location, null); conflict.Report.Warning (693, 3, Location, "Type parameter `{0}' has the same name as the type parameter from outer type `{1}'", GetSignatureForError (), conflict.CurrentType.GetSignatureForError ()); } } [System.Diagnostics.DebuggerDisplay ("{DisplayDebugInfo()}")] public class TypeParameterSpec : TypeSpec { public static readonly new TypeParameterSpec[] EmptyTypes = new TypeParameterSpec[0]; Variance variance; SpecialConstraint spec; int tp_pos; TypeSpec[] targs; TypeSpec[] ifaces_defined; TypeSpec effective_base; // // Creates type owned type parameter // public TypeParameterSpec (TypeSpec declaringType, int index, ITypeDefinition definition, SpecialConstraint spec, Variance variance, MetaType info) : base (MemberKind.TypeParameter, declaringType, definition, info, Modifiers.PUBLIC) { this.variance = variance; this.spec = spec; state &= ~StateFlags.Obsolete_Undetected; tp_pos = index; } // // Creates method owned type parameter // public TypeParameterSpec (int index, ITypeDefinition definition, SpecialConstraint spec, Variance variance, MetaType info) : this (null, index, definition, spec, variance, info) { } #region Properties public int DeclaredPosition { get { return tp_pos; } set { tp_pos = value; } } public bool HasSpecialConstructor { get { return (spec & SpecialConstraint.Constructor) != 0; } } public bool HasSpecialClass { get { return (spec & SpecialConstraint.Class) != 0; } } public bool HasSpecialStruct { get { return (spec & SpecialConstraint.Struct) != 0; } } public bool HasAnyTypeConstraint { get { return (spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0 || ifaces != null || targs != null || HasTypeConstraint; } } public bool HasTypeConstraint { get { var bt = BaseType.BuiltinType; return bt != BuiltinTypeSpec.Type.Object && bt != BuiltinTypeSpec.Type.ValueType; } } public override IList Interfaces { get { if ((state & StateFlags.InterfacesExpanded) == 0) { if (ifaces != null) { if (ifaces_defined == null) ifaces_defined = ifaces.ToArray (); for (int i = 0; i < ifaces_defined.Length; ++i ) { var iface_type = ifaces_defined[i]; var td = iface_type.MemberDefinition as TypeDefinition; if (td != null) td.DoExpandBaseInterfaces (); if (iface_type.Interfaces != null) { for (int ii = 0; ii < iface_type.Interfaces.Count; ++ii) { var ii_iface_type = iface_type.Interfaces [ii]; AddInterface (ii_iface_type); } } } } else if (ifaces_defined == null) { ifaces_defined = ifaces == null ? TypeSpec.EmptyTypes : ifaces.ToArray (); } // // Include all base type interfaces too, see ImportTypeBase for details // if (BaseType != null) { var td = BaseType.MemberDefinition as TypeDefinition; if (td != null) td.DoExpandBaseInterfaces (); if (BaseType.Interfaces != null) { foreach (var iface in BaseType.Interfaces) { AddInterface (iface); } } } state |= StateFlags.InterfacesExpanded; } return ifaces; } } // // Unexpanded interfaces list // public TypeSpec[] InterfacesDefined { get { if (ifaces_defined == null) { ifaces_defined = ifaces == null ? TypeSpec.EmptyTypes : ifaces.ToArray (); } return ifaces_defined.Length == 0 ? null : ifaces_defined; } set { ifaces_defined = value; if (value != null && value.Length != 0) ifaces = new List (value); } } public bool IsConstrained { get { return spec != SpecialConstraint.None || ifaces != null || targs != null || HasTypeConstraint; } } // // Returns whether the type parameter is known to be a reference type // public new bool IsReferenceType { get { if ((spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) return (spec & SpecialConstraint.Class) != 0; // // Full check is needed (see IsValueType for details) // if (HasTypeConstraint && TypeSpec.IsReferenceType (BaseType)) return true; if (targs != null) { foreach (var ta in targs) { // // Secondary special constraints are ignored (I am not sure why) // var tp = ta as TypeParameterSpec; if (tp != null && (tp.spec & (SpecialConstraint.Class | SpecialConstraint.Struct)) != 0) continue; if (TypeSpec.IsReferenceType (ta)) return true; } } return false; } } // // Returns whether the type parameter is known to be a value type // public new bool IsValueType { get { // // Even if structs/enums cannot be used directly as constraints // they can apear as constraint type when inheriting base constraint // which has dependant type parameter constraint which has been // inflated using value type // // class A : B { override void Foo () {} } // class B { virtual void Foo () where U : T {} } // if (HasSpecialStruct) return true; if (targs != null) { foreach (var ta in targs) { if (TypeSpec.IsValueType (ta)) return true; } } return false; } } public override string Name { get { return definition.Name; } } public bool IsMethodOwned { get { return DeclaringType == null; } } public SpecialConstraint SpecialConstraint { get { return spec; } set { spec = value; } } // // Types used to inflate the generic type // public new TypeSpec[] TypeArguments { get { return targs; } set { targs = value; } } public Variance Variance { get { return variance; } } #endregion public string DisplayDebugInfo () { var s = GetSignatureForError (); return IsMethodOwned ? s + "!!" : s + "!"; } // // Finds effective base class. The effective base class is always a class-type // public TypeSpec GetEffectiveBase () { if (HasSpecialStruct) return BaseType; // // If T has a class-type constraint C but no type-parameter constraints, its effective base class is C // if (BaseType != null && targs == null) { // // If T has a constraint V that is a value-type, use instead the most specific base type of V that is a class-type. // // LAMESPEC: Is System.ValueType always the most specific base type in this case? // // Note: This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method // are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method. // return BaseType.IsStruct ? BaseType.BaseType : BaseType; } if (effective_base != null) return effective_base; var types = new TypeSpec [HasTypeConstraint ? targs.Length + 1 : targs.Length]; for (int i = 0; i < targs.Length; ++i) { var t = targs [i]; // Same issue as above, inherited constraints can be of struct type if (t.IsStruct) { types [i] = t.BaseType; continue; } var tps = t as TypeParameterSpec; types [i] = tps != null ? tps.GetEffectiveBase () : t; } if (HasTypeConstraint) types [types.Length - 1] = BaseType; return effective_base = Convert.FindMostEncompassedType (types); } public override string GetSignatureForDocumentation (bool explicitName) { if (explicitName) return Name; var prefix = IsMethodOwned ? "``" : "`"; return prefix + DeclaredPosition; } public override string GetSignatureForError () { return Name; } // // Constraints have to match by definition but not position, used by // partial classes or methods // public bool HasSameConstraintsDefinition (TypeParameterSpec other) { if (spec != other.spec) return false; if (BaseType != other.BaseType) return false; if (!TypeSpecComparer.Override.IsSame (InterfacesDefined, other.InterfacesDefined)) return false; if (!TypeSpecComparer.Override.IsSame (targs, other.targs)) return false; return true; } // // Constraints have to match by using same set of types, used by // implicit interface implementation // public bool HasSameConstraintsImplementation (TypeParameterSpec other) { if (spec != other.spec) return false; // // It can be same base type or inflated type parameter // // interface I { void Foo where U : T; } // class A : I { void Foo where X : int {} } // bool found; if (!TypeSpecComparer.Override.IsEqual (BaseType, other.BaseType)) { found = false; if (other.targs != null) { foreach (var otarg in other.targs) { if (TypeSpecComparer.Override.IsEqual (BaseType, otarg)) { found = true; break; } } } else if (targs != null) { foreach (var targ in targs) { if (TypeSpecComparer.Override.IsEqual (targ, other.BaseType)) { found = true; break; } } } if (!found) return false; } // Check interfaces implementation -> definition if (InterfacesDefined != null) { // // Iterate over inflated interfaces // foreach (var iface in Interfaces) { found = false; if (other.InterfacesDefined != null) { foreach (var oiface in other.Interfaces) { if (TypeSpecComparer.Override.IsEqual (iface, oiface)) { found = true; break; } } } if (found) continue; if (other.targs != null) { foreach (var otarg in other.targs) { if (TypeSpecComparer.Override.IsEqual (iface, otarg)) { found = true; break; } } } if (!found) return false; } } // Check interfaces implementation <- definition if (other.InterfacesDefined != null) { // // Iterate over inflated interfaces // foreach (var oiface in other.Interfaces) { found = false; if (InterfacesDefined != null) { foreach (var iface in Interfaces) { if (TypeSpecComparer.Override.IsEqual (iface, oiface)) { found = true; break; } } } else if (targs != null) { foreach (var targ in targs) { if (TypeSpecComparer.Override.IsEqual (targ, oiface)) { found = true; break; } } } if (!found) return false; } } // Check type parameters implementation -> definition if (targs != null) { foreach (var targ in targs) { found = false; if (other.targs != null) { foreach (var otarg in other.targs) { if (TypeSpecComparer.Override.IsEqual (targ, otarg)) { found = true; break; } } } if (other.InterfacesDefined != null && !found) { foreach (var iface in other.Interfaces) { if (TypeSpecComparer.Override.IsEqual (iface, targ)) { found = true; break; } } } if (!found) found = TypeSpecComparer.Override.IsEqual (targ, other.BaseType); if (!found) return false; } } // Check type parameters implementation <- definition if (other.targs != null) { foreach (var otarg in other.targs) { // Ignore inflated type arguments, were checked above if (!otarg.IsGenericParameter) continue; if (targs == null) return false; found = false; foreach (var targ in targs) { if (TypeSpecComparer.Override.IsEqual (targ, otarg)) { found = true; break; } } if (!found) return false; } } return true; } public static TypeParameterSpec[] InflateConstraints (TypeParameterInflator inflator, TypeParameterSpec[] tparams) { return InflateConstraints (tparams, l => l, inflator); } public static TypeParameterSpec[] InflateConstraints (TypeParameterSpec[] tparams, Func inflatorFactory, T arg) { TypeParameterSpec[] constraints = null; TypeParameterInflator? inflator = null; for (int i = 0; i < tparams.Length; ++i) { var tp = tparams[i]; if (tp.HasTypeConstraint || tp.InterfacesDefined != null || tp.TypeArguments != null) { if (constraints == null) { constraints = new TypeParameterSpec[tparams.Length]; Array.Copy (tparams, constraints, constraints.Length); } // // Using a factory to avoid possibly expensive inflator build up // if (inflator == null) inflator = inflatorFactory (arg); constraints[i] = (TypeParameterSpec) constraints[i].InflateMember (inflator.Value); } } if (constraints == null) constraints = tparams; return constraints; } public void InflateConstraints (TypeParameterInflator inflator, TypeParameterSpec tps) { tps.BaseType = inflator.Inflate (BaseType); var defined = InterfacesDefined; if (defined != null) { tps.ifaces_defined = new TypeSpec[defined.Length]; for (int i = 0; i < defined.Length; ++i) tps.ifaces_defined [i] = inflator.Inflate (defined[i]); } else if (ifaces_defined == TypeSpec.EmptyTypes) { tps.ifaces_defined = TypeSpec.EmptyTypes; } var ifaces = Interfaces; if (ifaces != null) { tps.ifaces = new List (ifaces.Count); for (int i = 0; i < ifaces.Count; ++i) tps.ifaces.Add (inflator.Inflate (ifaces[i])); tps.state |= StateFlags.InterfacesExpanded; } if (targs != null) { tps.targs = new TypeSpec[targs.Length]; for (int i = 0; i < targs.Length; ++i) tps.targs[i] = inflator.Inflate (targs[i]); } } public override MemberSpec InflateMember (TypeParameterInflator inflator) { var tps = (TypeParameterSpec) MemberwiseClone (); #if DEBUG tps.ID += 1000000; #endif InflateConstraints (inflator, tps); return tps; } // // Populates type parameter members using type parameter constraints // The trick here is to be called late enough but not too late to // populate member cache with all members from other types // protected override void InitializeMemberCache (bool onlyTypes) { cache = new MemberCache (); if (targs != null) { foreach (var ta in targs) { var tps = ta as TypeParameterSpec; var b_type = tps == null ? ta : tps.GetEffectiveBase (); // // Find the most specific type when base type was inflated from base constraints // if (b_type != null && !b_type.IsStructOrEnum && TypeSpec.IsBaseClass (b_type, BaseType, false)) BaseType = b_type; } } // // For a type parameter the membercache is the union of the sets of members of the types // specified as a primary constraint or secondary constraint // if (BaseType.BuiltinType != BuiltinTypeSpec.Type.Object && BaseType.BuiltinType != BuiltinTypeSpec.Type.ValueType) { cache.AddBaseType (BaseType); } if (InterfacesDefined != null) { foreach (var iface_type in InterfacesDefined) { cache.AddInterface (iface_type); } } // // Import interfaces after base type to match behavior from ordinary classes // if (targs != null) { foreach (var ta in targs) { var tps = ta as TypeParameterSpec; var ifaces = tps == null ? ta.Interfaces : tps.InterfacesDefined; if (ifaces != null) { foreach (var iface_type in ifaces) { cache.AddInterface (iface_type); } } } } } public bool IsConvertibleToInterface (TypeSpec iface) { if (Interfaces != null) { foreach (var t in Interfaces) { if (t == iface) return true; } } if (TypeArguments != null) { foreach (var t in TypeArguments) { var tps = t as TypeParameterSpec; if (tps != null) { if (tps.IsConvertibleToInterface (iface)) return true; continue; } if (t.ImplementsInterface (iface, false)) return true; } } return false; } public static bool HasAnyTypeParameterTypeConstrained (IGenericMethodDefinition md) { var tps = md.TypeParameters; for (int i = 0; i < md.TypeParametersCount; ++i) { if (tps[i].HasAnyTypeConstraint) { return true; } } return false; } public static bool HasAnyTypeParameterConstrained (IGenericMethodDefinition md) { var tps = md.TypeParameters; for (int i = 0; i < md.TypeParametersCount; ++i) { if (tps[i].IsConstrained) { return true; } } return false; } public bool HasDependencyOn (TypeSpec type) { if (TypeArguments != null) { foreach (var targ in TypeArguments) { if (TypeSpecComparer.Override.IsEqual (targ, type)) return true; var tps = targ as TypeParameterSpec; if (tps != null && tps.HasDependencyOn (type)) return true; } } return false; } public override TypeSpec Mutate (TypeParameterMutator mutator) { return mutator.Mutate (this); } } public struct TypeParameterInflator { readonly TypeSpec type; readonly TypeParameterSpec[] tparams; readonly TypeSpec[] targs; readonly IModuleContext context; public TypeParameterInflator (TypeParameterInflator nested, TypeSpec type) : this (nested.context, type, nested.tparams, nested.targs) { } public TypeParameterInflator (IModuleContext context, TypeSpec type, TypeParameterSpec[] tparams, TypeSpec[] targs) { if (tparams.Length != targs.Length) throw new ArgumentException ("Invalid arguments"); this.context = context; this.tparams = tparams; this.targs = targs; this.type = type; } #region Properties public IModuleContext Context { get { return context; } } public TypeSpec TypeInstance { get { return type; } } // // Type parameters to inflate // public TypeParameterSpec[] TypeParameters { get { return tparams; } } #endregion public TypeSpec Inflate (TypeSpec type) { var tp = type as TypeParameterSpec; if (tp != null) return Inflate (tp); var ec = type as ElementTypeSpec; if (ec != null) { var et = Inflate (ec.Element); if (et != ec.Element) { var ac = ec as ArrayContainer; if (ac != null) return ArrayContainer.MakeType (context.Module, et, ac.Rank); if (ec is PointerContainer) return PointerContainer.MakeType (context.Module, et); throw new NotImplementedException (); } return ec; } if (type.Kind == MemberKind.MissingType) return type; // // When inflating a nested type, inflate its parent first // in case it's using same type parameters (was inflated within the type) // TypeSpec[] targs; int i = 0; if (type.IsNested) { var parent = Inflate (type.DeclaringType); // // Keep the inflated type arguments // targs = type.TypeArguments; // // When inflating imported nested type used inside same declaring type, we get TypeSpec // because the import cache helps us to catch it. However, that means we have to look at // type definition to get type argument (they are in fact type parameter in this case) // if (targs.Length == 0 && type.Arity > 0) targs = type.MemberDefinition.TypeParameters; // // Parent was inflated, find the same type on inflated type // to use same cache for nested types on same generic parent // type = MemberCache.FindNestedType (parent, type.Name, type.Arity); // // Handle the tricky case where parent shares local type arguments // which means inflating inflated type // // class Test { // public static Nested Foo () { return null; } // // public class Nested {} // } // // return type of Test.Foo() has to be Test.Nested // if (targs.Length > 0) { var inflated_targs = new TypeSpec[targs.Length]; for (; i < targs.Length; ++i) inflated_targs[i] = Inflate (targs[i]); type = type.MakeGenericType (context, inflated_targs); } return type; } // Nothing to do for non-generic type if (type.Arity == 0) return type; targs = new TypeSpec[type.Arity]; // // Inflating using outside type arguments, var v = new Foo (), class Foo {} // if (type is InflatedTypeSpec) { for (; i < targs.Length; ++i) targs[i] = Inflate (type.TypeArguments[i]); type = type.GetDefinition (); } else { // // Inflating parent using inside type arguments, class Foo { ITest foo; } // var args = type.MemberDefinition.TypeParameters; foreach (var ds_tp in args) targs[i++] = Inflate (ds_tp); } return type.MakeGenericType (context, targs); } public TypeSpec Inflate (TypeParameterSpec tp) { for (int i = 0; i < tparams.Length; ++i) if (tparams [i] == tp) return targs[i]; // This can happen when inflating nested types // without type arguments specified return tp; } } // // Before emitting any code we have to change all MVAR references to VAR // when the method is of generic type and has hoisted variables // public class TypeParameterMutator { readonly TypeParameters mvar; readonly TypeParameters var; readonly TypeParameterSpec[] src; Dictionary mutated_typespec; public TypeParameterMutator (TypeParameters mvar, TypeParameters var) { if (mvar.Count != var.Count) throw new ArgumentException (); this.mvar = mvar; this.var = var; } public TypeParameterMutator (TypeParameterSpec[] srcVar, TypeParameters destVar) { if (srcVar.Length != destVar.Count) throw new ArgumentException (); this.src = srcVar; this.var = destVar; } #region Properties public TypeParameters MethodTypeParameters { get { return mvar; } } #endregion public static TypeSpec GetMemberDeclaringType (TypeSpec type) { if (type is InflatedTypeSpec) { if (type.DeclaringType == null) return type.GetDefinition (); var parent = GetMemberDeclaringType (type.DeclaringType); type = MemberCache.GetMember (parent, type); } return type; } public TypeSpec Mutate (TypeSpec ts) { TypeSpec value; if (mutated_typespec != null && mutated_typespec.TryGetValue (ts, out value)) return value; value = ts.Mutate (this); if (mutated_typespec == null) mutated_typespec = new Dictionary (); mutated_typespec.Add (ts, value); return value; } public TypeParameterSpec Mutate (TypeParameterSpec tp) { if (mvar != null) { for (int i = 0; i < mvar.Count; ++i) { if (mvar[i].Type == tp) return var[i].Type; } } else { for (int i = 0; i < src.Length; ++i) { if (src[i] == tp) return var[i].Type; } } return tp; } public TypeSpec[] Mutate (TypeSpec[] targs) { TypeSpec[] mutated = new TypeSpec[targs.Length]; bool changed = false; for (int i = 0; i < targs.Length; ++i) { mutated[i] = Mutate (targs[i]); changed |= targs[i] != mutated[i]; } return changed ? mutated : targs; } } /// /// A TypeExpr which already resolved to a type parameter. /// public class TypeParameterExpr : TypeExpression { public TypeParameterExpr (TypeParameter type_parameter, Location loc) : base (type_parameter.Type, loc) { this.eclass = ExprClass.TypeParameter; } } public class InflatedTypeSpec : TypeSpec { TypeSpec[] targs; TypeParameterSpec[] constraints; readonly TypeSpec open_type; readonly IModuleContext context; public InflatedTypeSpec (IModuleContext context, TypeSpec openType, TypeSpec declaringType, TypeSpec[] targs) : base (openType.Kind, declaringType, openType.MemberDefinition, null, openType.Modifiers) { if (targs == null) throw new ArgumentNullException ("targs"); this.state &= ~SharedStateFlags; this.state |= (openType.state & SharedStateFlags); this.context = context; this.open_type = openType; this.targs = targs; foreach (var arg in targs) { if (arg.HasDynamicElement || arg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { state |= StateFlags.HasDynamicElement; break; } } if (open_type.Kind == MemberKind.MissingType) MemberCache = MemberCache.Empty; if ((open_type.Modifiers & Modifiers.COMPILER_GENERATED) != 0) state |= StateFlags.ConstraintsChecked; } #region Properties public override TypeSpec BaseType { get { if (cache == null || (state & StateFlags.PendingBaseTypeInflate) != 0) InitializeMemberCache (true); return base.BaseType; } } // // Inflated type parameters with constraints array, mapping with type arguments is based on index // public TypeParameterSpec[] Constraints { get { if (constraints == null) { constraints = TypeParameterSpec.InflateConstraints (MemberDefinition.TypeParameters, l => l.CreateLocalInflator (context), this); } return constraints; } } // // Used to cache expensive constraints validation on constructed types // public bool HasConstraintsChecked { get { return (state & StateFlags.ConstraintsChecked) != 0; } set { state = value ? state | StateFlags.ConstraintsChecked : state & ~StateFlags.ConstraintsChecked; } } public override IList Interfaces { get { if (cache == null) InitializeMemberCache (true); return base.Interfaces; } } public override bool IsExpressionTreeType { get { return (open_type.state & StateFlags.InflatedExpressionType) != 0; } } public override bool IsArrayGenericInterface { get { return (open_type.state & StateFlags.GenericIterateInterface) != 0; } } public override bool IsGenericTask { get { return (open_type.state & StateFlags.GenericTask) != 0; } } public override bool IsNullableType { get { return (open_type.state & StateFlags.InflatedNullableType) != 0; } } // // Types used to inflate the generic type // public override TypeSpec[] TypeArguments { get { return targs; } } #endregion public override bool AddInterface (TypeSpec iface) { var inflator = CreateLocalInflator (context); iface = inflator.Inflate (iface); if (iface == null) return false; return base.AddInterface (iface); } public static bool ContainsTypeParameter (TypeSpec type) { if (type.Kind == MemberKind.TypeParameter) return true; var element_container = type as ElementTypeSpec; if (element_container != null) return ContainsTypeParameter (element_container.Element); foreach (var t in type.TypeArguments) { if (ContainsTypeParameter (t)) { return true; } } return false; } public TypeParameterInflator CreateLocalInflator (IModuleContext context) { TypeParameterSpec[] tparams_full; TypeSpec[] targs_full = targs; if (IsNested) { // // Special case is needed when we are inflating an open type (nested type definition) // on inflated parent. Consider following case // // Foo.Bar => Foo.Bar // // Any later inflation of Foo.Bar has to also inflate T if used inside Bar // List merged_targs = null; List merged_tparams = null; var type = DeclaringType; do { if (type.TypeArguments.Length > 0) { if (merged_targs == null) { merged_targs = new List (); merged_tparams = new List (); if (targs.Length > 0) { merged_targs.AddRange (targs); merged_tparams.AddRange (open_type.MemberDefinition.TypeParameters); } } merged_tparams.AddRange (type.MemberDefinition.TypeParameters); merged_targs.AddRange (type.TypeArguments); } type = type.DeclaringType; } while (type != null); if (merged_targs != null) { // Type arguments are not in the right order but it should not matter in this case targs_full = merged_targs.ToArray (); tparams_full = merged_tparams.ToArray (); } else if (targs.Length == 0) { tparams_full = TypeParameterSpec.EmptyTypes; } else { tparams_full = open_type.MemberDefinition.TypeParameters; } } else if (targs.Length == 0) { tparams_full = TypeParameterSpec.EmptyTypes; } else { tparams_full = open_type.MemberDefinition.TypeParameters; } return new TypeParameterInflator (context, this, tparams_full, targs_full); } MetaType CreateMetaInfo () { // // Converts nested type arguments into right order // Foo.Bar => string, bool, int // var all = new List (); TypeSpec type = this; TypeSpec definition = type; do { if (type.GetDefinition().IsGeneric) { all.InsertRange (0, type.TypeArguments != TypeSpec.EmptyTypes ? type.TypeArguments.Select (l => l.GetMetaInfo ()) : type.MemberDefinition.TypeParameters.Select (l => l.GetMetaInfo ())); } definition = definition.GetDefinition (); type = type.DeclaringType; } while (type != null); return definition.GetMetaInfo ().MakeGenericType (all.ToArray ()); } public override void CheckObsoleteness (IMemberContext mc, Location loc) { base.CheckObsoleteness (mc, loc); foreach (var ta in TypeArguments) ta.CheckObsoleteness (mc, loc); } public override ObsoleteAttribute GetAttributeObsolete () { return open_type.GetAttributeObsolete (); } protected override bool IsNotCLSCompliant (out bool attrValue) { if (base.IsNotCLSCompliant (out attrValue)) return true; foreach (var ta in TypeArguments) { if (ta.MemberDefinition.CLSAttributeValue == false) return true; } return false; } public override TypeSpec GetDefinition () { return open_type; } public override MetaType GetMetaInfo () { if (info == null) info = CreateMetaInfo (); return info; } public override string GetSignatureForError () { if (IsNullableType) return targs[0].GetSignatureForError () + "?"; return base.GetSignatureForError (); } protected override string GetTypeNameSignature () { if (targs.Length == 0 || MemberDefinition is AnonymousTypeClass) return null; return "<" + TypeManager.CSharpName (targs) + ">"; } public bool HasDynamicArgument () { for (int i = 0; i < targs.Length; ++i) { var item = targs[i]; if (item.BuiltinType == BuiltinTypeSpec.Type.Dynamic) return true; if (item is InflatedTypeSpec) { if (((InflatedTypeSpec) item).HasDynamicArgument ()) return true; continue; } if (item.IsArray) { while (item.IsArray) { item = ((ArrayContainer) item).Element; } if (item.BuiltinType == BuiltinTypeSpec.Type.Dynamic) return true; } } return false; } protected override void InitializeMemberCache (bool onlyTypes) { if (cache == null) { var open_cache = onlyTypes ? open_type.MemberCacheTypes : open_type.MemberCache; // Surprisingly, calling MemberCache on open type could meantime create cache on this type // for imported type parameter constraints referencing nested type of this declaration if (cache == null) cache = new MemberCache (open_cache); } var inflator = CreateLocalInflator (context); // // Two stage inflate due to possible nested types recursive // references // // class A { // B b; // class B { // T Value; // } // } // // When resolving type of `b' members of `B' cannot be // inflated because are not yet available in membercache // if ((state & StateFlags.PendingMemberCacheMembers) == 0) { open_type.MemberCacheTypes.InflateTypes (cache, inflator); // // Inflate any implemented interfaces // if (open_type.Interfaces != null) { ifaces = new List (open_type.Interfaces.Count); foreach (var iface in open_type.Interfaces) { var iface_inflated = inflator.Inflate (iface); if (iface_inflated == null) continue; base.AddInterface (iface_inflated); } } // // Handles the tricky case of recursive nested base generic type // // class A : Base.Nested> { // class Nested {} // } // // When inflating A. base type is not yet known, secondary // inflation is required (not common case) once base scope // is known // if (open_type.BaseType == null) { if (IsClass) state |= StateFlags.PendingBaseTypeInflate; } else { BaseType = inflator.Inflate (open_type.BaseType); } } else if ((state & StateFlags.PendingBaseTypeInflate) != 0) { // // It can happen when resolving base type without being defined // which is not allowed to happen and will always lead to an error // // class B { class N {} } // class A : A {} // if (open_type.BaseType == null) return; BaseType = inflator.Inflate (open_type.BaseType); state &= ~StateFlags.PendingBaseTypeInflate; } if (onlyTypes) { state |= StateFlags.PendingMemberCacheMembers; return; } var tc = open_type.MemberDefinition as TypeDefinition; if (tc != null && !tc.HasMembersDefined) { // // Inflating MemberCache with undefined members // return; } if ((state & StateFlags.PendingBaseTypeInflate) != 0) { BaseType = inflator.Inflate (open_type.BaseType); state &= ~StateFlags.PendingBaseTypeInflate; } state &= ~StateFlags.PendingMemberCacheMembers; open_type.MemberCache.InflateMembers (cache, open_type, inflator); } public override TypeSpec Mutate (TypeParameterMutator mutator) { var targs = TypeArguments; if (targs != null) targs = mutator.Mutate (targs); var decl = DeclaringType; if (IsNested && DeclaringType.IsGenericOrParentIsGeneric) decl = mutator.Mutate (decl); if (targs == TypeArguments && decl == DeclaringType) return this; var mutated = (InflatedTypeSpec) MemberwiseClone (); if (decl != DeclaringType) { // Gets back MethodInfo in case of metaInfo was inflated //mutated.info = MemberCache.GetMember (DeclaringType.GetDefinition (), this).info; mutated.declaringType = decl; mutated.state |= StateFlags.PendingMetaInflate; } if (targs != null) { mutated.targs = targs; mutated.info = null; } return mutated; } } // // Tracks the type arguments when instantiating a generic type. It's used // by both type arguments and type parameters // public class TypeArguments { List args; TypeSpec[] atypes; public TypeArguments (params FullNamedExpression[] types) { this.args = new List (types); } public void Add (FullNamedExpression type) { args.Add (type); } /// /// We may only be used after Resolve() is called and return the fully /// resolved types. /// // TODO: Not needed, just return type from resolve public TypeSpec[] Arguments { get { return atypes; } set { atypes = value; } } public int Count { get { return args.Count; } } public virtual bool IsEmpty { get { return false; } } public List TypeExpressions { get { return this.args; } } public string GetSignatureForError() { StringBuilder sb = new StringBuilder (); for (int i = 0; i < Count; ++i) { var expr = args[i]; if (expr != null) sb.Append (expr.GetSignatureForError ()); if (i + 1 < Count) sb.Append (','); } return sb.ToString (); } /// /// Resolve the type arguments. /// public virtual bool Resolve (IMemberContext ec, bool allowUnbound) { if (atypes != null) return true; int count = args.Count; bool ok = true; atypes = new TypeSpec [count]; var errors = ec.Module.Compiler.Report.Errors; for (int i = 0; i < count; i++){ var te = args[i].ResolveAsType (ec); if (te == null) { ok = false; continue; } atypes[i] = te; if (te.IsStatic) { ec.Module.Compiler.Report.Error (718, args[i].Location, "`{0}': static classes cannot be used as generic arguments", te.GetSignatureForError ()); ok = false; } if (te.IsPointer || te.IsSpecialRuntimeType) { ec.Module.Compiler.Report.Error (306, args[i].Location, "The type `{0}' may not be used as a type argument", te.GetSignatureForError ()); ok = false; } } if (!ok || errors != ec.Module.Compiler.Report.Errors) atypes = null; return ok; } public TypeArguments Clone () { TypeArguments copy = new TypeArguments (); foreach (var ta in args) copy.args.Add (ta); return copy; } } public class UnboundTypeArguments : TypeArguments { Location loc; public UnboundTypeArguments (int arity, Location loc) : base (new FullNamedExpression[arity]) { this.loc = loc; } public override bool IsEmpty { get { return true; } } public override bool Resolve (IMemberContext mc, bool allowUnbound) { if (!allowUnbound) { mc.Module.Compiler.Report.Error (7003, loc, "Unbound generic name is not valid in this context"); } // Nothing to be resolved return true; } } public class TypeParameters { List names; TypeParameterSpec[] types; public TypeParameters () { names = new List (); } public TypeParameters (int count) { names = new List (count); } #region Properties public int Count { get { return names.Count; } } public TypeParameterSpec[] Types { get { return types; } } #endregion public void Add (TypeParameter tparam) { names.Add (tparam); } public void Add (TypeParameters tparams) { names.AddRange (tparams.names); } public void Create (TypeSpec declaringType, int parentOffset, TypeContainer parent) { types = new TypeParameterSpec[Count]; for (int i = 0; i < types.Length; ++i) { var tp = names[i]; tp.Create (declaringType, parent); types[i] = tp.Type; types[i].DeclaredPosition = i + parentOffset; if (tp.Variance != Variance.None && !(declaringType != null && (declaringType.Kind == MemberKind.Interface || declaringType.Kind == MemberKind.Delegate))) { parent.Compiler.Report.Error (1960, tp.Location, "Variant type parameters can only be used with interfaces and delegates"); } } } public void Define (GenericTypeParameterBuilder[] builders) { for (int i = 0; i < types.Length; ++i) { var tp = names[i]; tp.Define (builders [types [i].DeclaredPosition]); } } public TypeParameter this[int index] { get { return names [index]; } set { names[index] = value; } } public TypeParameter Find (string name) { foreach (var tp in names) { if (tp.Name == name) return tp; } return null; } public string[] GetAllNames () { return names.Select (l => l.Name).ToArray (); } public string GetSignatureForError () { StringBuilder sb = new StringBuilder (); for (int i = 0; i < Count; ++i) { if (i > 0) sb.Append (','); var name = names[i]; if (name != null) sb.Append (name.GetSignatureForError ()); } return sb.ToString (); } public void CheckPartialConstraints (Method part) { var partTypeParameters = part.CurrentTypeParameters; for (int i = 0; i < Count; i++) { var tp_a = names[i]; var tp_b = partTypeParameters [i]; if (tp_a.Constraints == null) { if (tp_b.Constraints == null) continue; } else if (tp_b.Constraints != null && tp_a.Type.HasSameConstraintsDefinition (tp_b.Type)) { continue; } part.Compiler.Report.SymbolRelatedToPreviousError (this[i].CurrentMemberDefinition.Location, ""); part.Compiler.Report.Error (761, part.Location, "Partial method declarations of `{0}' have inconsistent constraints for type parameter `{1}'", part.GetSignatureForError (), partTypeParameters[i].GetSignatureForError ()); } } public void UpdateConstraints (TypeDefinition part) { var partTypeParameters = part.MemberName.TypeParameters; for (int i = 0; i < Count; i++) { var tp = names [i]; if (tp.AddPartialConstraints (part, partTypeParameters [i])) continue; part.Compiler.Report.SymbolRelatedToPreviousError (this[i].CurrentMemberDefinition); part.Compiler.Report.Error (265, part.Location, "Partial declarations of `{0}' have inconsistent constraints for type parameter `{1}'", part.GetSignatureForError (), tp.GetSignatureForError ()); } } public void VerifyClsCompliance () { foreach (var tp in names) { tp.VerifyClsCompliance (); } } } // // A type expression of generic type with type arguments // class GenericTypeExpr : TypeExpr { TypeArguments args; TypeSpec open_type; /// /// Instantiate the generic type `t' with the type arguments `args'. /// Use this constructor if you already know the fully resolved /// generic type. /// public GenericTypeExpr (TypeSpec open_type, TypeArguments args, Location l) { this.open_type = open_type; loc = l; this.args = args; } public override string GetSignatureForError () { return type.GetSignatureForError (); } public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) { if (eclass != ExprClass.Unresolved) return type; if (!args.Resolve (mc, allowUnboundTypeArguments)) return null; TypeSpec[] atypes = args.Arguments; if (atypes == null) return null; // // Now bind the parameters // var inflated = open_type.MakeGenericType (mc, atypes); type = inflated; eclass = ExprClass.Type; // // The constraints can be checked only when full type hierarchy is known // if (!inflated.HasConstraintsChecked && mc.Module.HasTypesFullyDefined) { var constraints = inflated.Constraints; if (constraints != null) { var cc = new ConstraintChecker (mc); if (cc.CheckAll (open_type, atypes, constraints, loc)) { inflated.HasConstraintsChecked = true; } } } return type; } public override bool Equals (object obj) { GenericTypeExpr cobj = obj as GenericTypeExpr; if (cobj == null) return false; if ((type == null) || (cobj.type == null)) return false; return type == cobj.type; } public override int GetHashCode () { return base.GetHashCode (); } } // // Generic type with unbound type arguments, used for typeof (G<,,>) // class GenericOpenTypeExpr : TypeExpression { public GenericOpenTypeExpr (TypeSpec type, /*UnboundTypeArguments args,*/ Location loc) : base (type.GetDefinition (), loc) { } } struct ConstraintChecker { IMemberContext mc; bool recursive_checks; public ConstraintChecker (IMemberContext ctx) { this.mc = ctx; recursive_checks = false; } // // Checks the constraints of open generic type against type // arguments. This version is used for types which could not be // checked immediatelly during construction because the type // hierarchy was not yet fully setup (before Emit phase) // public static bool Check (IMemberContext mc, TypeSpec type, Location loc) { // // Check declaring type first if there is any // if (type.DeclaringType != null && !Check (mc, type.DeclaringType, loc)) return false; while (type is ElementTypeSpec) type = ((ElementTypeSpec) type).Element; if (type.Arity == 0) return true; var gtype = type as InflatedTypeSpec; if (gtype == null) return true; var constraints = gtype.Constraints; if (constraints == null) return true; if (gtype.HasConstraintsChecked) return true; var cc = new ConstraintChecker (mc); cc.recursive_checks = true; if (cc.CheckAll (gtype.GetDefinition (), type.TypeArguments, constraints, loc)) { gtype.HasConstraintsChecked = true; return true; } return false; } // // Checks all type arguments againts type parameters constraints // NOTE: It can run in probing mode when `this.mc' is null // public bool CheckAll (MemberSpec context, TypeSpec[] targs, TypeParameterSpec[] tparams, Location loc) { if (targs == null) return true; for (int i = 0; i < tparams.Length; i++) { var targ = targs[i]; if (!CheckConstraint (context, targ, tparams [i], loc)) return false; if (!recursive_checks) continue; if (!Check (mc, targ, loc)) return false; } return true; } bool CheckConstraint (MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, Location loc) { // // First, check the `class' and `struct' constraints. // if (tparam.HasSpecialClass && !TypeSpec.IsReferenceType (atype)) { if (mc != null) { mc.Module.Compiler.Report.Error (452, loc, "The type `{0}' must be a reference type in order to use it as type parameter `{1}' in the generic type or method `{2}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); } return false; } if (tparam.HasSpecialStruct && (!TypeSpec.IsValueType (atype) || atype.IsNullableType)) { if (mc != null) { mc.Module.Compiler.Report.Error (453, loc, "The type `{0}' must be a non-nullable value type in order to use it as type parameter `{1}' in the generic type or method `{2}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); } return false; } bool ok = true; // // Check the class constraint // if (tparam.HasTypeConstraint) { if (!CheckConversion (mc, context, atype, tparam, tparam.BaseType, loc)) { if (mc == null) return false; ok = false; } } // // Check the interfaces constraints // if (tparam.InterfacesDefined != null) { foreach (TypeSpec iface in tparam.InterfacesDefined) { if (!CheckConversion (mc, context, atype, tparam, iface, loc)) { if (mc == null) return false; ok = false; break; } } } // // Check the type parameter constraint // if (tparam.TypeArguments != null) { foreach (var ta in tparam.TypeArguments) { if (!CheckConversion (mc, context, atype, tparam, ta, loc)) { if (mc == null) return false; ok = false; break; } } } // // Finally, check the constructor constraint. // if (!tparam.HasSpecialConstructor) return ok; if (!HasDefaultConstructor (atype)) { if (mc != null) { mc.Module.Compiler.Report.SymbolRelatedToPreviousError (atype); mc.Module.Compiler.Report.Error (310, loc, "The type `{0}' must have a public parameterless constructor in order to use it as parameter `{1}' in the generic type or method `{2}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError ()); } return false; } return ok; } static bool HasDynamicTypeArgument (TypeSpec[] targs) { for (int i = 0; i < targs.Length; ++i) { var targ = targs [i]; if (targ.BuiltinType == BuiltinTypeSpec.Type.Dynamic) return true; if (HasDynamicTypeArgument (targ.TypeArguments)) return true; } return false; } bool CheckConversion (IMemberContext mc, MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, TypeSpec ttype, Location loc) { if (atype == ttype) return true; if (atype.IsGenericParameter) { var tps = (TypeParameterSpec) atype; if (tps.HasDependencyOn (ttype)) return true; if (Convert.ImplicitTypeParameterConversion (null, tps, ttype) != null) return true; } else if (TypeSpec.IsValueType (atype)) { if (atype.IsNullableType) { // // LAMESPEC: Only identity or base type ValueType or Object satisfy nullable type // if (TypeSpec.IsBaseClass (atype, ttype, false)) return true; } else { if (Convert.ImplicitBoxingConversion (null, atype, ttype) != null) return true; } } else { if (Convert.ImplicitReferenceConversionExists (atype, ttype) || Convert.ImplicitBoxingConversion (null, atype, ttype) != null) return true; } if (mc != null) { mc.Module.Compiler.Report.SymbolRelatedToPreviousError (tparam); if (atype.IsGenericParameter) { mc.Module.Compiler.Report.Error (314, loc, "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no boxing or type parameter conversion from `{0}' to `{3}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); } else if (TypeSpec.IsValueType (atype)) { if (atype.IsNullableType) { if (ttype.IsInterface) { mc.Module.Compiler.Report.Error (313, loc, "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. The nullable type `{0}' never satisfies interface constraint `{3}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); } else { mc.Module.Compiler.Report.Error (312, loc, "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. The nullable type `{0}' does not satisfy constraint `{3}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); } } else { mc.Module.Compiler.Report.Error (315, loc, "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no boxing conversion from `{0}' to `{3}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); } } else { mc.Module.Compiler.Report.Error (311, loc, "The type `{0}' cannot be used as type parameter `{1}' in the generic type or method `{2}'. There is no implicit reference conversion from `{0}' to `{3}'", atype.GetSignatureForError (), tparam.GetSignatureForError (), context.GetSignatureForError (), ttype.GetSignatureForError ()); } } return false; } static bool HasDefaultConstructor (TypeSpec atype) { var tp = atype as TypeParameterSpec; if (tp != null) { return tp.HasSpecialConstructor || tp.HasSpecialStruct; } if (atype.IsStruct || atype.IsEnum) return true; if (atype.IsAbstract) return false; var tdef = atype.GetDefinition (); var found = MemberCache.FindMember (tdef, MemberFilter.Constructor (ParametersCompiled.EmptyReadOnlyParameters), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly); return found != null && (found.Modifiers & Modifiers.PUBLIC) != 0; } } // // Implements C# type inference // class TypeInference { // // Tracks successful rate of type inference // int score; readonly Arguments arguments; readonly int arg_count; public TypeInference (Arguments arguments) { this.arguments = arguments; if (arguments != null) arg_count = arguments.Count; } public int InferenceScore { get { return score; } } public TypeSpec[] InferMethodArguments (ResolveContext ec, MethodSpec method) { var method_generic_args = method.GenericDefinition.TypeParameters; TypeInferenceContext context = new TypeInferenceContext (method_generic_args); if (!context.UnfixedVariableExists) return TypeSpec.EmptyTypes; AParametersCollection pd = method.Parameters; if (!InferInPhases (ec, context, pd)) return null; return context.InferredTypeArguments; } // // Implements method type arguments inference // bool InferInPhases (ResolveContext ec, TypeInferenceContext tic, AParametersCollection methodParameters) { int params_arguments_start; if (methodParameters.HasParams) { params_arguments_start = methodParameters.Count - 1; } else { params_arguments_start = arg_count; } TypeSpec [] ptypes = methodParameters.Types; // // The first inference phase // TypeSpec method_parameter = null; for (int i = 0; i < arg_count; i++) { Argument a = arguments [i]; if (a == null) continue; if (i < params_arguments_start) { method_parameter = methodParameters.Types [i]; } else if (i == params_arguments_start) { if (arg_count == params_arguments_start + 1 && TypeManager.HasElementType (a.Type)) method_parameter = methodParameters.Types [params_arguments_start]; else method_parameter = TypeManager.GetElementType (methodParameters.Types [params_arguments_start]); ptypes = (TypeSpec[]) ptypes.Clone (); ptypes [i] = method_parameter; } // // When a lambda expression, an anonymous method // is used an explicit argument type inference takes a place // AnonymousMethodExpression am = a.Expr as AnonymousMethodExpression; if (am != null) { if (am.ExplicitTypeInference (tic, method_parameter)) ++score; continue; } if (a.IsByRef) { score += tic.ExactInference (a.Type, method_parameter); continue; } if (a.Expr.Type == InternalType.NullLiteral) continue; if (TypeSpec.IsValueType (method_parameter)) { score += tic.LowerBoundInference (a.Type, method_parameter); continue; } // // Otherwise an output type inference is made // score += tic.OutputTypeInference (ec, a.Expr, method_parameter); } // // Part of the second phase but because it happens only once // we don't need to call it in cycle // bool fixed_any = false; if (!tic.FixIndependentTypeArguments (ec, ptypes, ref fixed_any)) return false; return DoSecondPhase (ec, tic, ptypes, !fixed_any); } bool DoSecondPhase (ResolveContext ec, TypeInferenceContext tic, TypeSpec[] methodParameters, bool fixDependent) { bool fixed_any = false; if (fixDependent && !tic.FixDependentTypes (ec, ref fixed_any)) return false; // If no further unfixed type variables exist, type inference succeeds if (!tic.UnfixedVariableExists) return true; if (!fixed_any && fixDependent) return false; // For all arguments where the corresponding argument output types // contain unfixed type variables but the input types do not, // an output type inference is made for (int i = 0; i < arg_count; i++) { // Align params arguments TypeSpec t_i = methodParameters [i >= methodParameters.Length ? methodParameters.Length - 1: i]; if (!t_i.IsDelegate) { if (!t_i.IsExpressionTreeType) continue; t_i = TypeManager.GetTypeArguments (t_i) [0]; } var mi = Delegate.GetInvokeMethod (t_i); TypeSpec rtype = mi.ReturnType; if (tic.IsReturnTypeNonDependent (mi, rtype)) { // It can be null for default arguments if (arguments[i] == null) continue; score += tic.OutputTypeInference (ec, arguments[i].Expr, t_i); } } return DoSecondPhase (ec, tic, methodParameters, true); } } public class TypeInferenceContext { protected enum BoundKind { Exact = 0, Lower = 1, Upper = 2 } struct BoundInfo : IEquatable { public readonly TypeSpec Type; public readonly BoundKind Kind; public BoundInfo (TypeSpec type, BoundKind kind) { this.Type = type; this.Kind = kind; } public override int GetHashCode () { return Type.GetHashCode (); } public Expression GetTypeExpression () { return new TypeExpression (Type, Location.Null); } #region IEquatable Members public bool Equals (BoundInfo other) { return Type == other.Type && Kind == other.Kind; } #endregion } readonly TypeSpec[] tp_args; readonly TypeSpec[] fixed_types; readonly List[] bounds; // TODO MemberCache: Could it be TypeParameterSpec[] ?? public TypeInferenceContext (TypeSpec[] typeArguments) { if (typeArguments.Length == 0) throw new ArgumentException ("Empty generic arguments"); fixed_types = new TypeSpec [typeArguments.Length]; for (int i = 0; i < typeArguments.Length; ++i) { if (typeArguments [i].IsGenericParameter) { if (bounds == null) { bounds = new List [typeArguments.Length]; tp_args = new TypeSpec [typeArguments.Length]; } tp_args [i] = typeArguments [i]; } else { fixed_types [i] = typeArguments [i]; } } } // // Used together with AddCommonTypeBound fo implement // 7.4.2.13 Finding the best common type of a set of expressions // public TypeInferenceContext () { fixed_types = new TypeSpec [1]; tp_args = new TypeSpec [1]; tp_args[0] = InternalType.Arglist; // it can be any internal type bounds = new List [1]; } public TypeSpec[] InferredTypeArguments { get { return fixed_types; } } public void AddCommonTypeBound (TypeSpec type) { AddToBounds (new BoundInfo (type, BoundKind.Lower), 0, false); } public void AddCommonTypeBoundAsync (TypeSpec type) { AddToBounds (new BoundInfo (type, BoundKind.Lower), 0, true); } void AddToBounds (BoundInfo bound, int index, bool voidAllowed) { // // Some types cannot be used as type arguments // if ((bound.Type.Kind == MemberKind.Void && !voidAllowed) || bound.Type.IsPointer || bound.Type.IsSpecialRuntimeType || bound.Type == InternalType.MethodGroup || bound.Type == InternalType.AnonymousMethod || bound.Type == InternalType.VarOutType) return; var a = bounds [index]; if (a == null) { a = new List (2); a.Add (bound); bounds [index] = a; return; } if (a.Contains (bound)) return; a.Add (bound); } bool AllTypesAreFixed (TypeSpec[] types) { foreach (TypeSpec t in types) { if (t.IsGenericParameter) { if (!IsFixed (t)) return false; continue; } if (t.IsGeneric && !AllTypesAreFixed (t.TypeArguments)) return false; } return true; } // // 26.3.3.8 Exact Inference // public int ExactInference (TypeSpec u, TypeSpec v) { // If V is an array type if (v.IsArray) { if (!u.IsArray) return 0; var ac_u = (ArrayContainer) u; var ac_v = (ArrayContainer) v; if (ac_u.Rank != ac_v.Rank) return 0; return ExactInference (ac_u.Element, ac_v.Element); } // // If V is constructed type and U is constructed type or dynamic // if (TypeManager.IsGenericType (v)) { if (u.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { var ga_v = v.TypeArguments; int score = 0; for (int i = 0; i < ga_v.Length; ++i) score += ExactInference (u, ga_v [i]); return System.Math.Min (1, score); } else { if (!TypeManager.IsGenericType (u) || v.MemberDefinition != u.MemberDefinition) return 0; var ga_u = u.TypeArguments; var ga_v = v.TypeArguments; if (u.TypeArguments.Length != u.TypeArguments.Length) return 0; int score = 0; for (int i = 0; i < ga_v.Length; ++i) score += ExactInference (ga_u [i], ga_v [i]); return System.Math.Min (1, score); } } // If V is one of the unfixed type arguments int pos = IsUnfixed (v); if (pos == -1) return 0; AddToBounds (new BoundInfo (u, BoundKind.Exact), pos, false); return 1; } public bool FixAllTypes (ResolveContext ec) { for (int i = 0; i < tp_args.Length; ++i) { if (!FixType (ec, i)) return false; } return true; } // // All unfixed type variables Xi are fixed for which all of the following hold: // a, There is at least one type variable Xj that depends on Xi // b, Xi has a non-empty set of bounds // public bool FixDependentTypes (ResolveContext ec, ref bool fixed_any) { for (int i = 0; i < tp_args.Length; ++i) { if (fixed_types[i] != null) continue; if (bounds[i] == null) continue; if (!FixType (ec, i)) return false; fixed_any = true; } return true; } // // All unfixed type variables Xi which depend on no Xj are fixed // public bool FixIndependentTypeArguments (ResolveContext ec, TypeSpec[] methodParameters, ref bool fixed_any) { var types_to_fix = new List (tp_args); for (int i = 0; i < methodParameters.Length; ++i) { TypeSpec t = methodParameters[i]; if (!t.IsDelegate) { if (!t.IsExpressionTreeType) continue; t = TypeManager.GetTypeArguments (t) [0]; } if (t.IsGenericParameter) continue; var invoke = Delegate.GetInvokeMethod (t); TypeSpec rtype = invoke.ReturnType; while (rtype.IsArray) rtype = ((ArrayContainer) rtype).Element; if (!rtype.IsGenericParameter && !TypeManager.IsGenericType (rtype)) continue; // Remove dependent types, they cannot be fixed yet RemoveDependentTypes (types_to_fix, rtype); } foreach (TypeSpec t in types_to_fix) { if (t == null) continue; int idx = IsUnfixed (t); if (idx >= 0 && !FixType (ec, idx)) { return false; } } fixed_any = types_to_fix.Count > 0; return true; } // // 26.3.3.10 Fixing // public bool FixType (ResolveContext ec, int i) { // It's already fixed if (fixed_types[i] != null) throw new InternalErrorException ("Type argument has been already fixed"); var candidates = bounds [i]; if (candidates == null) return false; if (candidates.Count == 1) { TypeSpec t = candidates[0].Type; if (t == InternalType.NullLiteral) return false; fixed_types [i] = t; return true; } // // The set of candidate types Uj starts out as the set of // all types in the set of bounds for Xi // var applicable = new bool [candidates.Count]; for (int ci = 0; ci < applicable.Length; ++ci) applicable [ci] = true; for (int ci = 0; ci < applicable.Length; ++ci) { var bound = candidates [ci]; int cii = 0; switch (bound.Kind) { case BoundKind.Exact: for (; cii != applicable.Length; ++cii) { if (ci == cii) continue; if (!applicable[cii]) continue; // // For each exact bound U of Xi all types Uj which are not identical // to U are removed from the candidate set // if (candidates [cii].Type != bound.Type) applicable[cii] = false; } break; case BoundKind.Lower: for (; cii != applicable.Length; ++cii) { if (ci == cii) continue; if (!applicable[cii]) continue; // // For each lower bound U of Xi all types Uj to which there is not an implicit conversion // from U are removed from the candidate set // if (!Convert.ImplicitConversionExists (ec, bound.GetTypeExpression (), candidates [cii].Type)) { applicable[cii] = false; } } break; case BoundKind.Upper: for (; cii != applicable.Length; ++cii) { if (ci == cii) continue; if (!applicable[cii]) continue; // // For each upper bound U of Xi all types Uj from which there is not an implicit conversion // to U are removed from the candidate set // if (!Convert.ImplicitConversionExists (ec, candidates[cii].GetTypeExpression (), bound.Type)) applicable[cii] = false; } break; } } TypeSpec best_candidate = null; for (int ci = 0; ci < applicable.Length; ++ci) { if (!applicable[ci]) continue; var bound = candidates [ci]; if (bound.Type == best_candidate) continue; int cii = 0; for (; cii < applicable.Length; ++cii) { if (ci == cii) continue; if (!applicable[cii]) continue; if (!Convert.ImplicitConversionExists (ec, candidates[cii].GetTypeExpression (), bound.Type)) break; } if (cii != applicable.Length) continue; // // We already have the best candidate, break if it's different (non-unique) // // Dynamic is never ambiguous as we prefer dynamic over other best candidate types // if (best_candidate != null) { if (best_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic) continue; if (bound.Type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && best_candidate != bound.Type) return false; } best_candidate = bound.Type; } if (best_candidate == null) return false; fixed_types[i] = best_candidate; return true; } public bool HasBounds (int pos) { return bounds[pos] != null; } // // Uses inferred or partially infered types to inflate delegate type argument. Returns // null when type parameter has not been fixed // public TypeSpec InflateGenericArgument (IModuleContext context, TypeSpec parameter) { var tp = parameter as TypeParameterSpec; if (tp != null) { // // Type inference works on generic arguments (MVAR) only // if (!tp.IsMethodOwned) return parameter; // // Ensure the type parameter belongs to same container // if (tp.DeclaredPosition < tp_args.Length && tp_args[tp.DeclaredPosition] == parameter) return fixed_types[tp.DeclaredPosition] ?? parameter; return parameter; } var gt = parameter as InflatedTypeSpec; if (gt != null) { var inflated_targs = new TypeSpec [gt.TypeArguments.Length]; for (int ii = 0; ii < inflated_targs.Length; ++ii) { var inflated = InflateGenericArgument (context, gt.TypeArguments [ii]); if (inflated == null) return null; inflated_targs[ii] = inflated; } return gt.GetDefinition ().MakeGenericType (context, inflated_targs); } var ac = parameter as ArrayContainer; if (ac != null) { var inflated = InflateGenericArgument (context, ac.Element); if (inflated != ac.Element) return ArrayContainer.MakeType (context.Module, inflated); } return parameter; } // // Tests whether all delegate input arguments are fixed and generic output type // requires output type inference // public bool IsReturnTypeNonDependent (MethodSpec invoke, TypeSpec returnType) { AParametersCollection d_parameters = invoke.Parameters; if (d_parameters.IsEmpty) return true; while (returnType.IsArray) returnType = ((ArrayContainer) returnType).Element; if (returnType.IsGenericParameter) { if (IsFixed (returnType)) return false; } else if (TypeManager.IsGenericType (returnType)) { TypeSpec[] g_args = TypeManager.GetTypeArguments (returnType); // At least one unfixed return type has to exist if (AllTypesAreFixed (g_args)) return false; } else { return false; } // All generic input arguments have to be fixed return AllTypesAreFixed (d_parameters.Types); } bool IsFixed (TypeSpec type) { return IsUnfixed (type) == -1; } int IsUnfixed (TypeSpec type) { if (!type.IsGenericParameter) return -1; for (int i = 0; i < tp_args.Length; ++i) { if (tp_args[i] == type) { if (fixed_types[i] != null) break; return i; } } return -1; } // // 26.3.3.9 Lower-bound Inference // public int LowerBoundInference (TypeSpec u, TypeSpec v) { return LowerBoundInference (u, v, false); } // // Lower-bound (false) or Upper-bound (true) inference based on inversed argument // int LowerBoundInference (TypeSpec u, TypeSpec v, bool inversed) { // If V is one of the unfixed type arguments int pos = IsUnfixed (v); if (pos != -1) { AddToBounds (new BoundInfo (u, inversed ? BoundKind.Upper : BoundKind.Lower), pos, false); return 1; } // If U is an array type var u_ac = u as ArrayContainer; if (u_ac != null) { var v_ac = v as ArrayContainer; if (v_ac != null) { if (u_ac.Rank != v_ac.Rank) return 0; if (TypeSpec.IsValueType (u_ac.Element)) return ExactInference (u_ac.Element, v_ac.Element); return LowerBoundInference (u_ac.Element, v_ac.Element, inversed); } if (u_ac.Rank != 1 || !v.IsArrayGenericInterface) return 0; var v_i = TypeManager.GetTypeArguments (v) [0]; if (TypeSpec.IsValueType (u_ac.Element)) return ExactInference (u_ac.Element, v_i); return LowerBoundInference (u_ac.Element, v_i); } if (v.IsGenericOrParentIsGeneric) { // // if V is a constructed type C and there is a unique type C // such that U is identical to, inherits from (directly or indirectly), // or implements (directly or indirectly) C // var u_candidates = new List (); var open_v = v.MemberDefinition; for (TypeSpec t = u; t != null; t = t.BaseType) { if (open_v == t.MemberDefinition) u_candidates.Add (t); // // Using this trick for dynamic type inference, the spec says the type arguments are "unknown" but // that would complicate the process a lot, instead I treat them as dynamic // if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) u_candidates.Add (t); } if (u.Interfaces != null) { foreach (var iface in u.Interfaces) { if (open_v == iface.MemberDefinition) u_candidates.Add (iface); } } TypeSpec[] unique_candidate_targs = null; var ga_v = TypeSpec.GetAllTypeArguments (v); foreach (TypeSpec u_candidate in u_candidates) { // // The unique set of types U1..Uk means that if we have an interface I, // class U : I, I then no type inference is made when inferring // type I by applying type U because T could be int or long // if (unique_candidate_targs != null) { TypeSpec[] second_unique_candidate_targs = TypeSpec.GetAllTypeArguments (u_candidate); if (TypeSpecComparer.Equals (unique_candidate_targs, second_unique_candidate_targs)) { unique_candidate_targs = second_unique_candidate_targs; continue; } // // Break when candidate arguments are ambiguous // return 0; } // // A candidate is dynamic type expression, to simplify things use dynamic // for all type parameter of this type. For methods like this one // // void M (IList, IList) // // dynamic becomes both T and U when the arguments are of dynamic type // if (u_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { unique_candidate_targs = new TypeSpec[ga_v.Length]; for (int i = 0; i < unique_candidate_targs.Length; ++i) unique_candidate_targs[i] = u_candidate; } else { unique_candidate_targs = TypeSpec.GetAllTypeArguments (u_candidate); } } if (unique_candidate_targs != null) { int score = 0; int tp_index = -1; TypeParameterSpec[] tps = null; for (int i = 0; i < unique_candidate_targs.Length; ++i) { if (tp_index < 0) { while (v.Arity == 0) v = v.DeclaringType; tps = v.MemberDefinition.TypeParameters; tp_index = tps.Length - 1; } Variance variance = tps [tp_index--].Variance; TypeSpec u_i = unique_candidate_targs [i]; if (variance == Variance.None || TypeSpec.IsValueType (u_i)) { if (ExactInference (u_i, ga_v [i]) == 0) ++score; } else { bool upper_bound = (variance == Variance.Contravariant && !inversed) || (variance == Variance.Covariant && inversed); if (LowerBoundInference (u_i, ga_v [i], upper_bound) == 0) ++score; } } return score; } } return 0; } // // 26.3.3.6 Output Type Inference // public int OutputTypeInference (ResolveContext ec, Expression e, TypeSpec t) { // If e is a lambda or anonymous method with inferred return type AnonymousMethodExpression ame = e as AnonymousMethodExpression; if (ame != null) { TypeSpec rt = ame.InferReturnType (ec, this, t); var invoke = Delegate.GetInvokeMethod (t); if (rt == null) { AParametersCollection pd = invoke.Parameters; return ame.Parameters.Count == pd.Count ? 1 : 0; } TypeSpec rtype = invoke.ReturnType; return LowerBoundInference (rt, rtype) + 1; } // // if E is a method group and T is a delegate type or expression tree type // return type Tb 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 for Tb. // if (e is MethodGroupExpr) { if (!t.IsDelegate) { if (!t.IsExpressionTreeType) return 0; t = TypeManager.GetTypeArguments (t)[0]; } var invoke = Delegate.GetInvokeMethod (t); TypeSpec rtype = invoke.ReturnType; if (!IsReturnTypeNonDependent (invoke, rtype)) return 0; // LAMESPEC: Standard does not specify that all methodgroup arguments // has to be fixed but it does not specify how to do recursive type inference // either. We choose the simple option and infer return type only // if all delegate generic arguments are fixed. TypeSpec[] param_types = new TypeSpec [invoke.Parameters.Count]; for (int i = 0; i < param_types.Length; ++i) { var inflated = InflateGenericArgument (ec, invoke.Parameters.Types[i]); if (inflated == null) return 0; param_types[i] = inflated; } MethodGroupExpr mg = (MethodGroupExpr) e; Arguments args = DelegateCreation.CreateDelegateMethodArguments (ec, invoke.Parameters, param_types, e.Location); mg = mg.OverloadResolve (ec, ref args, null, OverloadResolver.Restrictions.CovariantDelegate | OverloadResolver.Restrictions.ProbingOnly); if (mg == null) return 0; return LowerBoundInference (mg.BestCandidateReturnType, rtype) + 1; } // // if e is an expression with type U, then // a lower-bound inference is made from U for T // return LowerBoundInference (e.Type, t) * 2; } void RemoveDependentTypes (List types, TypeSpec returnType) { int idx = IsUnfixed (returnType); if (idx >= 0) { types [idx] = null; return; } if (TypeManager.IsGenericType (returnType)) { foreach (TypeSpec t in TypeManager.GetTypeArguments (returnType)) { RemoveDependentTypes (types, t); } } } public bool UnfixedVariableExists { get { foreach (TypeSpec ut in fixed_types) { if (ut == null) return true; } return false; } } } }