2 // generic.cs: Support classes for generics
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2003 Ximian, Inc.
10 using System.Reflection;
11 using System.Reflection.Emit;
12 using System.Globalization;
13 using System.Collections;
16 namespace Mono.CSharp {
18 public enum SpecialConstraint
26 // Tracks the constraints for a type parameter
28 public class Constraints : GenericConstraints {
30 ArrayList constraints;
34 // name is the identifier, constraints is an arraylist of
35 // Expressions (with types) or `true' for the constructor constraint.
37 public Constraints (string name, ArrayList constraints,
41 this.constraints = constraints;
45 public string TypeParameter {
51 bool has_ctor_constraint;
52 bool has_reference_type;
54 TypeExpr class_constraint;
55 ArrayList iface_constraints;
56 int num_constraints, first_constraint;
57 Type class_constraint_type;
58 Type[] iface_constraint_types;
60 public bool HasConstructorConstraint {
61 get { return has_ctor_constraint; }
64 public bool Resolve (DeclSpace ds)
66 iface_constraints = new ArrayList ();
68 foreach (object obj in constraints) {
69 if (has_ctor_constraint) {
70 Report.Error (401, loc,
71 "The new() constraint must be last.");
75 if (obj is SpecialConstraint) {
76 SpecialConstraint sc = (SpecialConstraint) obj;
78 if (sc == SpecialConstraint.Constructor) {
79 if (!has_value_type) {
80 has_ctor_constraint = true;
85 451, loc, "The new () constraint " +
86 "cannot be used with the `struct' " +
91 if ((num_constraints > 0) || has_reference_type ||
93 Report.Error (449, loc,
94 "The `class' or `struct' " +
95 "constraint must be first");
99 if (sc == SpecialConstraint.ReferenceType)
100 has_reference_type = true;
102 has_value_type = true;
106 TypeExpr expr = ds.ResolveTypeExpr ((Expression) obj, false, loc);
110 if (expr is TypeParameterExpr) {
111 Report.Error (700, loc,
112 "`{0}': naked type parameters cannot " +
113 "be used as bounds", expr.Name);
117 if (expr.IsInterface)
118 iface_constraints.Add (expr);
119 else if (class_constraint != null) {
120 Report.Error (406, loc,
121 "`{0}': the class constraint for `{1}' " +
122 "must come before any other constraints.",
125 } else if (has_reference_type || has_value_type) {
126 Report.Error (450, loc, "`{0}': cannot specify both " +
127 "a constraint class and the `class' " +
128 "or `struct' constraint.", expr.Name);
131 class_constraint = expr;
139 public bool ResolveTypes (EmitContext ec)
141 iface_constraint_types = new Type [iface_constraints.Count];
143 for (int i = 0; i < iface_constraints.Count; i++) {
144 TypeExpr iface_constraint = (TypeExpr) iface_constraints [i];
145 Type resolved = iface_constraint.ResolveType (ec);
146 if (resolved == null)
149 for (int j = 0; j < i; j++) {
150 if (!iface_constraint_types [j].Equals (resolved))
153 Report.Error (405, loc,
154 "Duplicate constraint `{0}' for type " +
155 "parameter `{1}'.", resolved, name);
159 iface_constraint_types [i] = resolved;
162 if (class_constraint != null) {
163 class_constraint_type = class_constraint.ResolveType (ec);
164 if (class_constraint_type == null)
167 if (class_constraint_type.IsSealed) {
168 Report.Error (701, loc,
169 "`{0}' is not a valid bound. Bounds " +
170 "must be interfaces or non sealed " +
171 "classes", class_constraint_type);
175 if ((class_constraint_type == TypeManager.array_type) ||
176 (class_constraint_type == TypeManager.delegate_type) ||
177 (class_constraint_type == TypeManager.enum_type) ||
178 (class_constraint_type == TypeManager.value_type) ||
179 (class_constraint_type == TypeManager.object_type)) {
180 Report.Error (702, loc,
181 "Bound cannot be special class `{0}'",
182 class_constraint_type);
187 if (has_reference_type)
188 class_constraint_type = TypeManager.object_type;
189 else if (has_value_type)
190 class_constraint_type = TypeManager.value_type;
195 public void Define (GenericTypeParameterBuilder type)
197 if (has_ctor_constraint)
198 type.Mono_SetConstructorConstraint ();
199 if (has_reference_type)
200 type.Mono_SetReferenceTypeConstraint ();
201 else if (has_value_type)
202 type.Mono_SetValueTypeConstraint ();
205 bool GenericConstraints.HasConstructor {
206 get { return has_ctor_constraint; }
209 bool GenericConstraints.IsReferenceType {
210 get { return has_reference_type; }
213 bool GenericConstraints.IsValueType {
214 get { return has_value_type; }
217 bool GenericConstraints.HasClassConstraint {
218 get { return class_constraint_type != null; }
221 Type GenericConstraints.ClassConstraint {
222 get { return class_constraint_type; }
225 Type[] GenericConstraints.InterfaceConstraints {
226 get { return iface_constraint_types; }
231 // This type represents a generic type parameter
233 public class TypeParameter : IMemberContainer {
235 Constraints constraints;
237 GenericTypeParameterBuilder type;
239 public TypeParameter (string name, Constraints constraints, Location loc)
242 this.constraints = constraints;
252 public Location Location {
258 public Constraints Constraints {
264 public bool HasConstructorConstraint {
266 if (constraints != null)
267 return constraints.HasConstructorConstraint;
279 public bool Resolve (DeclSpace ds)
281 if (constraints != null)
282 return constraints.Resolve (ds);
287 public void Define (GenericTypeParameterBuilder type)
290 Type[] ifaces = null;
291 if (constraints != null)
292 constraints.Define (type);
293 TypeManager.AddTypeParameter (type, this);
296 public bool DefineType (EmitContext ec)
298 if (constraints != null) {
299 if (!constraints.ResolveTypes (ec))
302 GenericConstraints gc = (GenericConstraints) constraints;
304 if (gc.HasClassConstraint)
305 type.SetBaseTypeConstraint (gc.ClassConstraint);
307 type.SetInterfaceConstraints (gc.InterfaceConstraints);
308 TypeManager.RegisterBuilder (type, gc.InterfaceConstraints);
318 IMemberContainer IMemberContainer.Parent {
322 bool IMemberContainer.IsInterface {
326 MemberList IMemberContainer.GetMembers (MemberTypes mt, BindingFlags bf)
328 return FindMembers (mt, bf, null, null);
331 MemberCache IMemberContainer.MemberCache {
335 public MemberList FindMembers (MemberTypes mt, BindingFlags bf,
336 MemberFilter filter, object criteria)
338 if (constraints == null)
339 return MemberList.Empty;
341 ArrayList members = new ArrayList ();
343 GenericConstraints gc = (GenericConstraints) constraints;
345 if (gc.HasClassConstraint) {
346 MemberList list = TypeManager.FindMembers (
347 gc.ClassConstraint, mt, bf, filter, criteria);
349 members.AddRange (list);
352 foreach (Type t in gc.InterfaceConstraints) {
353 MemberList list = TypeManager.FindMembers (
354 t, mt, bf, filter, criteria);
356 members.AddRange (list);
359 return new MemberList (members);
362 public override string ToString ()
364 return "TypeParameter[" + name + "]";
369 // This type represents a generic type parameter reference.
371 // These expressions are born in a fully resolved state.
373 public class TypeParameterExpr : TypeExpr {
374 TypeParameter type_parameter;
376 public override string Name {
378 return type_parameter.Name;
382 public TypeParameter TypeParameter {
384 return type_parameter;
388 public TypeParameterExpr (TypeParameter type_parameter, Location loc)
390 this.type_parameter = type_parameter;
394 public override TypeExpr DoResolveAsTypeStep (EmitContext ec)
396 type = type_parameter.Type;
401 public void Error_CannotUseAsUnmanagedType (Location loc)
403 Report.Error (-203, loc, "Can not use type parameter as unamanged type");
407 public class TypeArguments {
408 public readonly Location Location;
414 public TypeArguments (Location loc)
416 args = new ArrayList ();
420 public void Add (Expression type)
423 throw new InvalidOperationException ();
428 public void Add (TypeArguments new_args)
431 throw new InvalidOperationException ();
433 args.AddRange (new_args.args);
436 public string[] GetDeclarations ()
438 string[] ret = new string [args.Count];
439 for (int i = 0; i < args.Count; i++) {
440 SimpleName sn = args [i] as SimpleName;
446 Report.Error (81, Location, "Type parameter declaration " +
447 "must be an identifier not a type");
453 public Type[] Arguments {
459 public bool HasTypeArguments {
461 return has_type_args;
471 public override string ToString ()
473 StringBuilder s = new StringBuilder ();
475 int count = args.Count;
476 for (int i = 0; i < count; i++){
478 // FIXME: Use TypeManager.CSharpname once we have the type
480 s.Append (args [i].ToString ());
484 return s.ToString ();
487 public bool Resolve (EmitContext ec)
489 DeclSpace ds = ec.DeclSpace;
490 int count = args.Count;
493 atypes = new Type [count];
495 for (int i = 0; i < count; i++){
496 TypeExpr te = ds.ResolveTypeExpr (
497 (Expression) args [i], false, Location);
502 if (te is TypeParameterExpr)
503 has_type_args = true;
504 atypes [i] = te.ResolveType (ec);
506 if (atypes [i] == null) {
507 Report.Error (246, Location, "Cannot find type `{0}'",
516 public class ConstructedType : TypeExpr {
517 string name, full_name;
519 Type[] gen_params, atypes;
522 public ConstructedType (string name, TypeArguments args, Location l)
525 this.name = MemberName.MakeName (name, args.Count);
528 eclass = ExprClass.Type;
529 full_name = name + "<" + args.ToString () + ">";
532 public ConstructedType (string name, TypeParameter[] type_params, Location l)
533 : this (type_params, l)
538 full_name = name + "<" + args.ToString () + ">";
541 protected ConstructedType (TypeArguments args, Location l)
546 eclass = ExprClass.Type;
549 protected ConstructedType (TypeParameter[] type_params, Location l)
553 args = new TypeArguments (l);
554 foreach (TypeParameter type_param in type_params)
555 args.Add (new TypeParameterExpr (type_param, l));
557 eclass = ExprClass.Type;
560 public ConstructedType (Type t, TypeParameter[] type_params, Location l)
561 : this (type_params, l)
563 gt = t.GetGenericTypeDefinition ();
565 this.name = gt.FullName;
566 full_name = gt.FullName + "<" + args.ToString () + ">";
569 public ConstructedType (Type t, TypeArguments args, Location l)
572 gt = t.GetGenericTypeDefinition ();
574 this.name = gt.FullName;
575 full_name = gt.FullName + "<" + args.ToString () + ">";
578 public TypeArguments TypeArguments {
582 protected string DeclarationName {
584 StringBuilder sb = new StringBuilder ();
585 sb.Append (gt.FullName);
587 for (int i = 0; i < gen_params.Length; i++) {
590 sb.Append (gen_params [i]);
593 return sb.ToString ();
597 protected bool CheckConstraint (EmitContext ec, Type ptype, Expression expr,
600 if (TypeManager.HasGenericArguments (ctype)) {
601 Type[] types = TypeManager.GetTypeArguments (ctype);
603 TypeArguments new_args = new TypeArguments (loc);
605 for (int i = 0; i < types.Length; i++) {
608 if (t.IsGenericParameter) {
609 int pos = t.GenericParameterPosition;
610 t = args.Arguments [pos];
612 new_args.Add (new TypeExpression (t, loc));
615 ctype = new ConstructedType (ctype, new_args, loc).ResolveType (ec);
620 return Convert.ImplicitStandardConversionExists (expr, ctype);
623 protected bool CheckConstraints (EmitContext ec, int index)
625 Type atype = atypes [index];
626 Type ptype = gen_params [index];
631 Expression aexpr = new EmptyExpression (atype);
633 Type parent = ptype.BaseType;
636 // First, check the `class' and `struct' constraints.
638 if (parent == TypeManager.object_type) {
639 if (!atype.IsClass) {
640 Report.Error (452, loc, "The type `{0}' must be " +
641 "a reference type in order to use it " +
642 "as type parameter `{1}' in the " +
643 "generic type or method `{2}'.",
644 atype, ptype, DeclarationName);
647 } else if (parent == TypeManager.value_type) {
648 if (!atype.IsValueType) {
649 Report.Error (453, loc, "The type `{0}' must be " +
650 "a value type in order to use it " +
651 "as type parameter `{1}' in the " +
652 "generic type or method `{2}'.",
653 atype, ptype, DeclarationName);
659 // The class constraint comes next.
661 if ((parent != null) && (parent != TypeManager.object_type)) {
662 if (!CheckConstraint (ec, ptype, aexpr, parent)) {
663 Report.Error (309, loc, "The type `{0}' must be " +
664 "convertible to `{1}' in order to " +
665 "use it as parameter `{2}' in the " +
666 "generic type or method `{3}'",
667 atype, parent, ptype, DeclarationName);
673 // Now, check the interface constraints.
675 foreach (Type itype in TypeManager.GetInterfaces (ptype)) {
676 if (!CheckConstraint (ec, ptype, aexpr, itype)) {
677 Report.Error (309, loc, "The type `{0}' must be " +
678 "convertible to `{1}' in order to " +
679 "use it as parameter `{2}' in the " +
680 "generic type or method `{3}'",
681 atype, itype, ptype, DeclarationName);
687 // Finally, check the constructor constraint.
690 if (!TypeManager.HasConstructorConstraint (ptype))
693 MethodGroupExpr mg = Expression.MemberLookup (
694 ec, atype, ".ctor", MemberTypes.Constructor,
695 BindingFlags.Public | BindingFlags.Instance |
696 BindingFlags.DeclaredOnly, loc)
699 if (atype.IsAbstract || (mg == null) || !mg.IsInstance) {
700 Report.Error (310, loc, "The type `{0}' must have a public " +
701 "parameterless constructor in order to use it " +
702 "as parameter `{1}' in the generic type or " +
703 "method `{2}'", atype, ptype, DeclarationName);
710 public override TypeExpr DoResolveAsTypeStep (EmitContext ec)
716 // First, resolve the generic type.
719 Type nested = ec.DeclSpace.FindNestedType (loc, name, out ds);
720 if (nested != null) {
721 gt = nested.GetGenericTypeDefinition ();
723 TypeArguments new_args = new TypeArguments (loc);
724 foreach (TypeParameter param in ds.TypeParameters)
725 new_args.Add (new TypeParameterExpr (param, loc));
735 SimpleName sn = new SimpleName (name, loc);
736 TypeExpr resolved = sn.ResolveAsTypeTerminal (ec);
737 if ((resolved == null) || (resolved.Type == null)) {
738 Report.Error (246, loc,
739 "The type or namespace name `{0}<...>' "+
740 "could not be found", Basename);
746 Report.Error (246, loc, "Cannot find type `{0}'<...>",
751 num_args = TypeManager.GetNumberOfTypeArguments (t);
753 Report.Error (308, loc,
754 "The non-generic type `{0}' cannot " +
755 "be used with type arguments.",
756 TypeManager.CSharpName (t));
760 gt = t.GetGenericTypeDefinition ();
764 public override Type ResolveType (EmitContext ec)
768 if (DoResolveAsTypeStep (ec) == null)
772 // Resolve the arguments.
774 if (args.Resolve (ec) == false)
777 gen_params = gt.GetGenericArguments ();
778 atypes = args.Arguments;
780 if (atypes.Length != gen_params.Length) {
781 Report.Error (305, loc,
782 "Using the generic type `{0}' " +
783 "requires {1} type arguments",
784 TypeManager.GetFullName (gt),
789 for (int i = 0; i < gen_params.Length; i++) {
790 if (!CheckConstraints (ec, i))
795 // Now bind the parameters.
797 type = gt.BindGenericParameters (atypes);
801 public Expression GetSimpleName (EmitContext ec)
803 return new SimpleName (Basename, args, loc);
806 public override bool CheckAccessLevel (DeclSpace ds)
808 return ds.CheckAccessLevel (gt);
811 public override bool AsAccessible (DeclSpace ds, int flags)
813 return ds.AsAccessible (gt, flags);
816 public override bool IsClass {
817 get { return gt.IsClass; }
820 public override bool IsValueType {
821 get { return gt.IsValueType; }
824 public override bool IsInterface {
825 get { return gt.IsInterface; }
828 public override bool IsSealed {
829 get { return gt.IsSealed; }
832 public override bool IsAttribute {
833 get { return false; }
836 public override bool Equals (object obj)
838 ConstructedType cobj = obj as ConstructedType;
842 if ((type == null) || (cobj.type == null))
845 return type == cobj.type;
848 public string Basename {
850 int pos = name.LastIndexOf ('`');
852 return name.Substring (0, pos);
858 public override string Name {
865 public class GenericMethod : DeclSpace
867 public GenericMethod (NamespaceEntry ns, TypeContainer parent,
868 MemberName name, Location l)
869 : base (ns, parent, name, null, l)
872 public override TypeBuilder DefineType ()
874 throw new Exception ();
877 public override bool Define ()
879 for (int i = 0; i < TypeParameters.Length; i++)
880 if (!TypeParameters [i].Resolve (Parent))
886 public bool Define (MethodBuilder mb)
891 GenericTypeParameterBuilder[] gen_params;
892 string[] names = MemberName.TypeArguments.GetDeclarations ();
893 gen_params = mb.DefineGenericParameters (names);
894 for (int i = 0; i < TypeParameters.Length; i++)
895 TypeParameters [i].Define (gen_params [i]);
900 public bool DefineType (EmitContext ec, MethodBuilder mb)
902 for (int i = 0; i < TypeParameters.Length; i++)
903 if (!TypeParameters [i].DefineType (ec))
909 public override bool DefineMembers (TypeContainer parent)
914 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
915 MemberFilter filter, object criteria)
917 throw new Exception ();
920 public override MemberCache MemberCache {
922 throw new Exception ();
926 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
931 protected override void VerifyObsoleteAttribute()
936 public override AttributeTargets AttributeTargets {
938 return AttributeTargets.Method | AttributeTargets.ReturnValue;
943 public class DefaultValueExpression : Expression
946 LocalTemporary temp_storage;
948 public DefaultValueExpression (Expression expr, Location loc)
954 public override Expression DoResolve (EmitContext ec)
956 type = ec.DeclSpace.ResolveType (expr, false, loc);
960 if (type.IsGenericParameter || TypeManager.IsValueType (type))
961 temp_storage = new LocalTemporary (ec, type);
963 eclass = ExprClass.Variable;
967 public override void Emit (EmitContext ec)
969 if (temp_storage != null) {
970 temp_storage.AddressOf (ec, AddressOp.LoadStore);
971 ec.ig.Emit (OpCodes.Initobj, type);
972 temp_storage.Emit (ec);
974 ec.ig.Emit (OpCodes.Ldnull);