X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fgmcs%2Fgeneric.cs;h=5406444662418957cd26daa44c1cf61fbe3ca863;hb=1733851d28faad34184ad6cbf25992e33f483e44;hp=6aa080e08fe0b8562f8b975c53dee09f95dab183;hpb=0ee7506e6e89ca0a1eb58d1e62c372f4ea67df6f;p=mono.git diff --git a/mcs/gmcs/generic.cs b/mcs/gmcs/generic.cs index 6aa080e08fe..54064446624 100644 --- a/mcs/gmcs/generic.cs +++ b/mcs/gmcs/generic.cs @@ -1,10 +1,13 @@ // -// generic.cs: Support classes for generics +// generic.cs: Generics support // -// Author: -// Miguel de Icaza (miguel@ximian.com) +// Authors: Martin Baulig (martin@ximian.com) +// Miguel de Icaza (miguel@ximian.com) // -// (C) 2003 Ximian, Inc. +// Licensed under the terms of the GNU GPL +// +// (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// (C) 2004 Novell, Inc // using System; using System.Reflection; @@ -12,9 +15,106 @@ using System.Reflection.Emit; using System.Globalization; using System.Collections; using System.Text; +using System.Text.RegularExpressions; namespace Mono.CSharp { + public abstract class GenericConstraints { + public abstract GenericParameterAttributes Attributes { + get; + } + + public bool HasConstructorConstraint { + get { return (Attributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0; } + } + + public bool HasReferenceTypeConstraint { + get { return (Attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0; } + } + + public bool HasValueTypeConstraint { + get { return (Attributes & GenericParameterAttributes.ValueTypeConstraint) != 0; } + } + + public virtual bool HasClassConstraint { + get { return ClassConstraint != null; } + } + + public abstract Type ClassConstraint { + get; + } + + public abstract Type[] InterfaceConstraints { + get; + } + + public abstract Type EffectiveBaseClass { + get; + } + + // + // Returns whether the type parameter is "known to be a reference type". + // + public virtual bool IsReferenceType { + get { + if (HasReferenceTypeConstraint) + return true; + if (HasValueTypeConstraint) + return false; + + if (ClassConstraint != null) { + if (ClassConstraint.IsValueType) + return false; + + if (ClassConstraint != TypeManager.object_type) + return true; + } + + foreach (Type t in InterfaceConstraints) { + if (!t.IsGenericParameter) + continue; + + GenericConstraints gc = TypeManager.GetTypeParameterConstraints (t); + if ((gc != null) && gc.IsReferenceType) + return true; + } + + return false; + } + } + + // + // Returns whether the type parameter is "known to be a value type". + // + public virtual bool IsValueType { + get { + if (HasValueTypeConstraint) + return true; + if (HasReferenceTypeConstraint) + return false; + + if (ClassConstraint != null) { + if (!ClassConstraint.IsValueType) + return false; + + if (ClassConstraint != TypeManager.value_type) + return true; + } + + foreach (Type t in InterfaceConstraints) { + if (!t.IsGenericParameter) + continue; + + GenericConstraints gc = TypeManager.GetTypeParameterConstraints (t); + if ((gc != null) && gc.IsValueType) + return true; + } + + return false; + } + } + } + public enum SpecialConstraint { Constructor, @@ -48,27 +148,22 @@ namespace Mono.CSharp { } } - bool has_ctor_constraint; - bool has_reference_type; - bool has_value_type; + GenericParameterAttributes attrs; TypeExpr class_constraint; ArrayList iface_constraints; ArrayList type_param_constraints; - int num_constraints, first_constraint; + int num_constraints; Type class_constraint_type; Type[] iface_constraint_types; + Type effective_base_type; - public bool HasConstructorConstraint { - get { return has_ctor_constraint; } - } - - public bool Resolve (DeclSpace ds) + public bool Resolve (EmitContext ec) { iface_constraints = new ArrayList (); type_param_constraints = new ArrayList (); foreach (object obj in constraints) { - if (has_ctor_constraint) { + if (HasConstructorConstraint) { Report.Error (401, loc, "The new() constraint must be last."); return false; @@ -78,8 +173,8 @@ namespace Mono.CSharp { SpecialConstraint sc = (SpecialConstraint) obj; if (sc == SpecialConstraint.Constructor) { - if (!has_value_type) { - has_ctor_constraint = true; + if (!HasValueTypeConstraint) { + attrs |= GenericParameterAttributes.DefaultConstructorConstraint; continue; } @@ -90,8 +185,7 @@ namespace Mono.CSharp { return false; } - if ((num_constraints > 0) || has_reference_type || - has_value_type) { + if ((num_constraints > 0) || HasReferenceTypeConstraint || HasValueTypeConstraint) { Report.Error (449, loc, "The `class' or `struct' " + "constraint must be first"); @@ -99,13 +193,33 @@ namespace Mono.CSharp { } if (sc == SpecialConstraint.ReferenceType) - has_reference_type = true; + attrs |= GenericParameterAttributes.ReferenceTypeConstraint; else - has_value_type = true; + attrs |= GenericParameterAttributes.ValueTypeConstraint; continue; } - TypeExpr expr = ds.ResolveTypeExpr ((Expression) obj, false, loc); + int errors = Report.Errors; + FullNamedExpression fn = ((Expression) obj).ResolveAsTypeStep (ec); + + if (fn == null) { + if (errors != Report.Errors) + return false; + + Report.Error (246, loc, "Cannot find type '{0}'", obj); + return false; + } + + TypeExpr expr; + ConstructedType cexpr = fn as ConstructedType; + if (cexpr != null) { + if (!cexpr.ResolveConstructedType (ec)) + return false; + + expr = cexpr; + } else + expr = fn.ResolveAsTypeTerminal (ec); + if (expr == null) return false; @@ -120,7 +234,7 @@ namespace Mono.CSharp { "must come before any other constraints.", expr.Name, name); return false; - } else if (has_reference_type || has_value_type) { + } else if (HasReferenceTypeConstraint || HasValueTypeConstraint) { Report.Error (450, loc, "`{0}': cannot specify both " + "a constraint class and the `class' " + "or `struct' constraint.", expr.Name); @@ -142,7 +256,7 @@ namespace Mono.CSharp { if (constraints == null) return true; - if (constraints.IsValueType) { + if (constraints.HasValueTypeConstraint) { Report.Error (456, loc, "Type parameter `{0}' has " + "the `struct' constraint, so it cannot " + "be used as a constraint for `{1}'", @@ -170,6 +284,18 @@ namespace Mono.CSharp { public bool ResolveTypes (EmitContext ec) { + if (effective_base_type != null) + return true; + + foreach (object obj in constraints) { + ConstructedType cexpr = obj as ConstructedType; + if (cexpr == null) + continue; + + if (!cexpr.CheckConstraints (ec)) + return false; + } + foreach (TypeParameterExpr expr in type_param_constraints) { Hashtable seen = new Hashtable (); if (!CheckTypeParameterConstraints (expr.TypeParameter, seen)) @@ -179,46 +305,62 @@ namespace Mono.CSharp { ArrayList list = new ArrayList (); foreach (TypeExpr iface_constraint in iface_constraints) { - Type resolved = iface_constraint.ResolveType (ec); - if (resolved == null) - return false; - foreach (Type type in list) { - if (!type.Equals (resolved)) + if (!type.Equals (iface_constraint.Type)) continue; Report.Error (405, loc, "Duplicate constraint `{0}' for type " + - "parameter `{1}'.", resolved, name); + "parameter `{1}'.", iface_constraint.Type, + name); return false; } - list.Add (resolved); + TypeExpr te = iface_constraint.ResolveAsTypeTerminal (ec); + if (te == null) + return false; + + list.Add (te.Type); } foreach (TypeParameterExpr expr in type_param_constraints) { - Type resolved = expr.ResolveType (ec); - if (resolved == null) - return false; - foreach (Type type in list) { - if (!type.Equals (resolved)) + if (!type.Equals (expr.Type)) continue; Report.Error (405, loc, "Duplicate constraint `{0}' for type " + - "parameter `{1}'.", resolved, name); + "parameter `{1}'.", expr.Type, name); return false; } - list.Add (resolved); + list.Add (expr.Type); } - iface_constraint_types = new Type [list.Count]; - list.CopyTo (iface_constraint_types, 0); + ArrayList new_list = new ArrayList (); + foreach (Type iface in list) { + if (new_list.Contains (iface)) + continue; + + new_list.Add (iface); + + Type [] implementing = TypeManager.GetInterfaces (iface); + + foreach (Type imp in implementing) { + if (!new_list.Contains (imp)) + new_list.Add (imp); + } + } + + iface_constraint_types = new Type [new_list.Count]; + new_list.CopyTo (iface_constraint_types, 0); if (class_constraint != null) { - class_constraint_type = class_constraint.ResolveType (ec); + TypeExpr te = class_constraint.ResolveAsTypeTerminal (ec); + if (te == null) + return false; + + class_constraint_type = te.Type; if (class_constraint_type == null) return false; @@ -242,10 +384,12 @@ namespace Mono.CSharp { } } - if (has_reference_type) - class_constraint_type = TypeManager.object_type; - else if (has_value_type) - class_constraint_type = TypeManager.value_type; + if (class_constraint_type != null) + effective_base_type = class_constraint_type; + else if (HasValueTypeConstraint) + effective_base_type = TypeManager.value_type; + else + effective_base_type = TypeManager.object_type; return true; } @@ -266,7 +410,7 @@ namespace Mono.CSharp { if (constraints == null) return true; - if (IsValueType && constraints.HasClassConstraint) { + if (HasValueTypeConstraint && constraints.HasClassConstraint) { Report.Error (455, loc, "Type parameter `{0}' inherits " + "conflicting constraints `{1}' and `{2}'", name, constraints.ClassConstraint, @@ -280,8 +424,8 @@ namespace Mono.CSharp { Type t2 = constraints.ClassConstraint; TypeExpr e2 = constraints.class_constraint; - if (!Convert.ImplicitReferenceConversionExists (e1, t2) && - !Convert.ImplicitReferenceConversionExists (e2, t1)) { + if (!Convert.ImplicitReferenceConversionExists (ec, e1, t2) && + !Convert.ImplicitReferenceConversionExists (ec, e2, t1)) { Report.Error (455, loc, "Type parameter `{0}' inherits " + "conflicting constraints `{1}' and `{2}'", @@ -303,38 +447,29 @@ namespace Mono.CSharp { public void Define (GenericTypeParameterBuilder type) { - if (has_ctor_constraint) - type.Mono_SetConstructorConstraint (); - if (has_reference_type) - type.Mono_SetReferenceTypeConstraint (); - else if (has_value_type) - type.Mono_SetValueTypeConstraint (); + type.SetGenericParameterAttributes (attrs); } - public bool HasConstructor { - get { return has_ctor_constraint; } + public override GenericParameterAttributes Attributes { + get { return attrs; } } - public bool IsReferenceType { - get { return has_reference_type; } + public override bool HasClassConstraint { + get { return class_constraint != null; } } - public bool IsValueType { - get { return has_value_type; } - } - - public bool HasClassConstraint { - get { return class_constraint_type != null; } - } - - public Type ClassConstraint { + public override Type ClassConstraint { get { return class_constraint_type; } } - public Type[] InterfaceConstraints { + public override Type[] InterfaceConstraints { get { return iface_constraint_types; } } + public override Type EffectiveBaseClass { + get { return effective_base_type; } + } + internal bool IsSubclassOf (Type t) { if ((class_constraint_type != null) && @@ -354,19 +489,12 @@ namespace Mono.CSharp { public bool CheckInterfaceMethod (EmitContext ec, GenericConstraints gc) { - if (!ResolveTypes (ec)) + if (gc.Attributes != attrs) return false; - if (gc.HasConstructor != HasConstructor) - return false; - if (gc.IsReferenceType != IsReferenceType) - return false; - if (gc.IsValueType != IsValueType) + if (HasClassConstraint != gc.HasClassConstraint) return false; - if (gc.HasClassConstraint != HasClassConstraint) - return false; - - if (HasClassConstraint && !gc.ClassConstraint.Equals (ClassConstraint)) + if (HasClassConstraint && !TypeManager.IsEqual (gc.ClassConstraint, ClassConstraint)) return false; int gc_icount = gc.InterfaceConstraints != null ? @@ -380,7 +508,7 @@ namespace Mono.CSharp { foreach (Type iface in gc.InterfaceConstraints) { bool ok = false; foreach (Type check in InterfaceConstraints) { - if (iface.Equals (check)) { + if (TypeManager.IsEqual (iface, check)) { ok = true; break; } @@ -397,28 +525,25 @@ namespace Mono.CSharp { // // This type represents a generic type parameter // - public class TypeParameter : IMemberContainer { + public class TypeParameter : MemberCore, IMemberContainer { string name; + GenericConstraints gc; Constraints constraints; Location loc; GenericTypeParameterBuilder type; - public TypeParameter (string name, Constraints constraints, Location loc) + public TypeParameter (TypeContainer parent, string name, + Constraints constraints, Location loc) + : base (parent, new MemberName (name), null, loc) { this.name = name; this.constraints = constraints; this.loc = loc; } - public string Name { - get { - return name; - } - } - - public Location Location { + public GenericConstraints GenericConstraints { get { - return loc; + return gc != null ? gc : constraints; } } @@ -445,19 +570,41 @@ namespace Mono.CSharp { public bool Resolve (DeclSpace ds) { - if (constraints != null) - return constraints.Resolve (ds); + if (constraints != null) { + if (!constraints.Resolve (ds.EmitContext)) { + constraints = null; + return false; + } + } return true; } public void Define (GenericTypeParameterBuilder type) { + if (this.type != null) + throw new InvalidOperationException (); + this.type = type; - Type[] ifaces = null; + TypeManager.AddTypeParameter (type, this); + } + + public void DefineConstraints () + { if (constraints != null) constraints.Define (type); - TypeManager.AddTypeParameter (type, this); + } + + public bool ResolveType (EmitContext ec) + { + if (constraints != null) { + if (!constraints.ResolveTypes (ec)) { + constraints = null; + return false; + } + } + + return true; } public bool DefineType (EmitContext ec) @@ -468,7 +615,8 @@ namespace Mono.CSharp { public bool DefineType (EmitContext ec, MethodBuilder builder, MethodInfo implementing, bool is_override) { - GenericConstraints gc; + if (!ResolveType (ec)) + return false; if (implementing != null) { if (is_override && (constraints != null)) { @@ -485,14 +633,27 @@ namespace Mono.CSharp { mb = mb.GetGenericMethodDefinition (); int pos = type.GenericParameterPosition; - ParameterData pd = Invocation.GetParameterData (mb); - gc = pd.GenericConstraints (pos); + ParameterData pd = TypeManager.GetParameterData (mb); + GenericConstraints temp_gc = pd.GenericConstraints (pos); Type mparam = mb.GetGenericArguments () [pos]; - if (((constraints != null) && (gc == null)) || - ((constraints == null) && (gc != null)) || - ((constraints != null) && - !constraints.CheckInterfaceMethod (ec, gc))) { + if (temp_gc != null) + gc = new InflatedConstraints (temp_gc, implementing.DeclaringType); + else if (constraints != null) + gc = new InflatedConstraints (constraints, implementing.DeclaringType); + + bool ok = true; + if (constraints != null) { + if (temp_gc == null) + ok = false; + else if (!constraints.CheckInterfaceMethod (ec, gc)) + ok = false; + } else { + if (!is_override && (temp_gc != null)) + ok = false; + } + + if (!ok) { Report.SymbolRelatedToPreviousError (implementing); Report.Error ( @@ -506,11 +667,6 @@ namespace Mono.CSharp { return false; } } else { - if (constraints != null) { - if (!constraints.ResolveTypes (ec)) - return false; - } - gc = (GenericConstraints) constraints; } @@ -534,11 +690,76 @@ namespace Mono.CSharp { return true; } + public bool UpdateConstraints (EmitContext ec, Constraints new_constraints) + { + // + // We're used in partial generic type definitions. + // If `check' is false, we just encountered the first ClassPart which has + // constraints - they become our "real" constraints. + // Otherwise we're called after the type parameters have already been defined + // and check whether the constraints are the same in all parts. + // + if (type == null) + throw new InvalidOperationException (); + + if (constraints == null) { + new_constraints = constraints; + return true; + } else if (new_constraints == null) + return true; + + if (!new_constraints.Resolve (ec)) + return false; + if (!new_constraints.ResolveTypes (ec)) + return false; + + return constraints.CheckInterfaceMethod (ec, new_constraints); + } + + public override string DocCommentHeader { + get { + throw new InvalidOperationException ( + "Unexpected attempt to get doc comment from " + this.GetType () + "."); + } + } + + // + // MemberContainer + // + + public override bool Define () + { + return true; + } + + protected override void VerifyObsoleteAttribute () + { } + + public override void ApplyAttributeBuilder (Attribute a, + CustomAttributeBuilder cb) + { } + + public override AttributeTargets AttributeTargets { + get { + return (AttributeTargets) 0; + } + } + + public override string[] ValidAttributeTargets { + get { + return new string [0]; + } + } + // // IMemberContainer // - IMemberContainer IMemberContainer.ParentContainer { + string IMemberContainer.Name { + get { return Name; } + } + + MemberCache IMemberContainer.BaseCache { get { return null; } } @@ -597,6 +818,80 @@ namespace Mono.CSharp { { return "TypeParameter[" + name + "]"; } + + protected class InflatedConstraints : GenericConstraints + { + GenericConstraints gc; + Type base_type; + Type class_constraint; + Type[] iface_constraints; + Type[] dargs; + + public InflatedConstraints (GenericConstraints gc, Type declaring) + { + this.gc = gc; + + dargs = TypeManager.GetTypeArguments (declaring); + + ArrayList list = new ArrayList (); + if (gc.HasClassConstraint) + list.Add (inflate (gc.ClassConstraint)); + foreach (Type iface in gc.InterfaceConstraints) + list.Add (inflate (iface)); + + bool has_class_constr = false; + if (list.Count > 0) { + Type first = (Type) list [0]; + has_class_constr = !first.IsInterface && !first.IsGenericParameter; + } + + if ((list.Count > 0) && has_class_constr) { + class_constraint = (Type) list [0]; + iface_constraints = new Type [list.Count - 1]; + list.CopyTo (1, iface_constraints, 0, list.Count - 1); + } else { + iface_constraints = new Type [list.Count]; + list.CopyTo (iface_constraints, 0); + } + + if (HasValueTypeConstraint) + base_type = TypeManager.value_type; + else if (class_constraint != null) + base_type = class_constraint; + else + base_type = TypeManager.object_type; + } + + Type inflate (Type t) + { + if (t == null) + return null; + if (t.IsGenericParameter) + return dargs [t.GenericParameterPosition]; + if (t.IsGenericInstance) { + t = t.GetGenericTypeDefinition (); + t = t.BindGenericParameters (dargs); + } + + return t; + } + + public override GenericParameterAttributes Attributes { + get { return gc.Attributes; } + } + + public override Type ClassConstraint { + get { return class_constraint; } + } + + public override Type EffectiveBaseClass { + get { return base_type; } + } + + public override Type[] InterfaceConstraints { + get { return iface_constraints; } + } + } } // @@ -613,6 +908,12 @@ namespace Mono.CSharp { } } + public override string FullName { + get { + return type_parameter.Name; + } + } + public TypeParameter TypeParameter { get { return type_parameter; @@ -625,7 +926,7 @@ namespace Mono.CSharp { this.loc = loc; } - public override TypeExpr DoResolveAsTypeStep (EmitContext ec) + protected override TypeExpr DoResolveAsTypeStep (EmitContext ec) { type = type_parameter.Type; @@ -636,6 +937,11 @@ namespace Mono.CSharp { get { return false; } } + public override bool CheckAccessLevel (DeclSpace ds) + { + return true; + } + public void Error_CannotUseAsUnmanagedType (Location loc) { Report.Error (-203, loc, "Can not use type parameter as unamanged type"); @@ -741,58 +1047,49 @@ namespace Mono.CSharp { public bool Resolve (EmitContext ec) { - DeclSpace ds = ec.DeclSpace; int count = args.Count; bool ok = true; atypes = new Type [count]; for (int i = 0; i < count; i++){ - TypeExpr te = ds.ResolveTypeExpr ( - (Expression) args [i], false, Location); + TypeExpr te = ((Expression) args [i]).ResolveAsTypeTerminal (ec); if (te == null) { ok = false; continue; } if (te is TypeParameterExpr) has_type_args = true; - atypes [i] = te.ResolveType (ec); - if (atypes [i] == null) { - Report.Error (246, Location, "Cannot find type `{0}'", - te.Name); - ok = false; + if (te.Type.IsPointer) { + Report.Error (306, Location, "The type `{0}' may not be used " + + "as a type argument.", TypeManager.CSharpName (te.Type)); + return false; } + + atypes [i] = te.Type; } return ok; } } public class ConstructedType : TypeExpr { - string name, full_name; + string full_name; + FullNamedExpression name; TypeArguments args; Type[] gen_params, atypes; Type gt; - public ConstructedType (string name, TypeArguments args, Location l) + public ConstructedType (FullNamedExpression fname, TypeArguments args, Location l) { loc = l; - this.name = MemberName.MakeName (name, args.Count); + this.name = fname; this.args = args; eclass = ExprClass.Type; full_name = name + "<" + args.ToString () + ">"; } - public ConstructedType (string name, TypeParameter[] type_params, Location l) - : this (type_params, l) - { - loc = l; - - this.name = name; - full_name = name + "<" + args.ToString () + ">"; - } - protected ConstructedType (TypeArguments args, Location l) { loc = l; @@ -817,7 +1114,7 @@ namespace Mono.CSharp { { gt = t.GetGenericTypeDefinition (); - this.name = gt.FullName; + this.name = new TypeExpression (gt, l); full_name = gt.FullName + "<" + args.ToString () + ">"; } @@ -826,7 +1123,7 @@ namespace Mono.CSharp { { gt = t.GetGenericTypeDefinition (); - this.name = gt.FullName; + this.name = new TypeExpression (gt, l); full_name = gt.FullName + "<" + args.ToString () + ">"; } @@ -867,12 +1164,13 @@ namespace Mono.CSharp { new_args.Add (new TypeExpression (t, loc)); } - ctype = new ConstructedType (ctype, new_args, loc).ResolveType (ec); - if (ctype == null) + TypeExpr ct = new ConstructedType (ctype, new_args, loc); + if (ct.ResolveAsTypeStep (ec) == null) return false; + ctype = ct.Type; } - return Convert.ImplicitStandardConversionExists (expr, ctype); + return Convert.ImplicitStandardConversionExists (ec, expr, ctype); } protected bool CheckConstraints (EmitContext ec, int index) @@ -885,41 +1183,53 @@ namespace Mono.CSharp { Expression aexpr = new EmptyExpression (atype); - Type parent = ptype.BaseType; + GenericConstraints gc = TypeManager.GetTypeParameterConstraints (ptype); + if (gc == null) + return true; + + bool is_class, is_struct; + if (atype.IsGenericParameter) { + GenericConstraints agc = TypeManager.GetTypeParameterConstraints (atype); + if (agc != null) { + is_class = agc.HasReferenceTypeConstraint; + is_struct = agc.HasValueTypeConstraint; + } else { + is_class = is_struct = false; + } + } else { + is_class = atype.IsClass; + is_struct = atype.IsValueType; + } // // First, check the `class' and `struct' constraints. // - if (parent == TypeManager.object_type) { - if (!atype.IsClass) { - 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, ptype, DeclarationName); - return false; - } - } else if (parent == TypeManager.value_type) { - if (!atype.IsValueType) { - Report.Error (453, loc, "The type `{0}' must be " + - "a value type in order to use it " + - "as type parameter `{1}' in the " + - "generic type or method `{2}'.", - atype, ptype, DeclarationName); - return false; - } + if (gc.HasReferenceTypeConstraint && !is_class) { + 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, ptype, DeclarationName); + return false; + } else if (gc.HasValueTypeConstraint && !is_struct) { + Report.Error (453, loc, "The type `{0}' must be " + + "a value type in order to use it " + + "as type parameter `{1}' in the " + + "generic type or method `{2}'.", + atype, ptype, DeclarationName); + return false; } // // The class constraint comes next. // - if ((parent != null) && (parent != TypeManager.object_type)) { - if (!CheckConstraint (ec, ptype, aexpr, parent)) { + if (gc.HasClassConstraint) { + if (!CheckConstraint (ec, ptype, aexpr, gc.ClassConstraint)) { Report.Error (309, loc, "The type `{0}' must be " + "convertible to `{1}' in order to " + "use it as parameter `{2}' in the " + "generic type or method `{3}'", - atype, parent, ptype, DeclarationName); + atype, gc.ClassConstraint, ptype, DeclarationName); return false; } } @@ -927,7 +1237,7 @@ namespace Mono.CSharp { // // Now, check the interface constraints. // - foreach (Type it in TypeManager.GetInterfaces (ptype)) { + foreach (Type it in gc.InterfaceConstraints) { Type itype; if (it.IsGenericParameter) itype = atypes [it.GenericParameterPosition]; @@ -948,10 +1258,10 @@ namespace Mono.CSharp { // Finally, check the constructor constraint. // - if (!TypeManager.HasConstructorConstraint (ptype)) + if (!gc.HasConstructorConstraint) return true; - if (TypeManager.IsBuiltinType (atype)) + if (TypeManager.IsBuiltinType (atype) || atype.IsValueType) return true; MethodGroupExpr mg = Expression.MemberLookup ( @@ -971,45 +1281,37 @@ namespace Mono.CSharp { return true; } - public override TypeExpr DoResolveAsTypeStep (EmitContext ec) + protected override TypeExpr DoResolveAsTypeStep (EmitContext ec) { - if (gt != null) - return this; - - // - // First, resolve the generic type. - // - DeclSpace ds; - Type nested = ec.DeclSpace.FindNestedType (loc, name, out ds); - if (nested != null) { - gt = nested.GetGenericTypeDefinition (); + if (!ResolveConstructedType (ec)) + return null; - TypeArguments new_args = new TypeArguments (loc); - foreach (TypeParameter param in ds.TypeParameters) - new_args.Add (new TypeParameterExpr (param, loc)); - new_args.Add (args); + return this; + } - args = new_args; - return this; + public bool CheckConstraints (EmitContext ec) + { + for (int i = 0; i < gen_params.Length; i++) { + if (!CheckConstraints (ec, i)) + return false; } - Type t; + return true; + } + + public bool ResolveConstructedType (EmitContext ec) + { + if (type != null) + return true; + if (gt != null) + return DoResolveType (ec); + int num_args; + Type t = name.Type; - SimpleName sn = new SimpleName (name, loc); - TypeExpr resolved = sn.ResolveAsTypeTerminal (ec); - if ((resolved == null) || (resolved.Type == null)) { - Report.Error (246, loc, - "The type or namespace name `{0}<...>' "+ - "could not be found", Basename); - return null; - } - - t = resolved.Type; - if (t == null) { - Report.Error (246, loc, "Cannot find type `{0}'<...>", - Basename); - return null; + if (t == null) { + Report.Error (246, loc, "Cannot find type `{0}'<...>", Name); + return false; } num_args = TypeManager.GetNumberOfTypeArguments (t); @@ -1018,25 +1320,20 @@ namespace Mono.CSharp { "The non-generic type `{0}' cannot " + "be used with type arguments.", TypeManager.CSharpName (t)); - return null; + return false; } gt = t.GetGenericTypeDefinition (); - return this; + return DoResolveType (ec); } - public override Type ResolveType (EmitContext ec) + bool DoResolveType (EmitContext ec) { - if (type != null) - return type; - if (DoResolveAsTypeStep (ec) == null) - return null; - // // Resolve the arguments. // if (args.Resolve (ec) == false) - return null; + return false; gen_params = gt.GetGenericArguments (); atypes = args.Arguments; @@ -1047,24 +1344,19 @@ namespace Mono.CSharp { "requires {1} type arguments", TypeManager.GetFullName (gt), gen_params.Length); - return null; - } - - for (int i = 0; i < gen_params.Length; i++) { - if (!CheckConstraints (ec, i)) - return null; + return false; } // // Now bind the parameters. // type = gt.BindGenericParameters (atypes); - return type; + return true; } public Expression GetSimpleName (EmitContext ec) { - return new SimpleName (Basename, args, loc); + return this; } public override bool CheckAccessLevel (DeclSpace ds) @@ -1093,10 +1385,6 @@ namespace Mono.CSharp { get { return gt.IsSealed; } } - public override bool IsAttribute { - get { return false; } - } - public override bool Equals (object obj) { ConstructedType cobj = obj as ConstructedType; @@ -1109,17 +1397,19 @@ namespace Mono.CSharp { return type == cobj.type; } - public string Basename { + public override int GetHashCode () + { + return base.GetHashCode (); + } + + public override string Name { get { - int pos = name.LastIndexOf ('`'); - if (pos >= 0) - return name.Substring (0, pos); - else - return name; + return full_name; } } - public override string Name { + + public override string FullName { get { return full_name; } @@ -1140,8 +1430,10 @@ namespace Mono.CSharp { public override bool Define () { + ec = new EmitContext (this, this, Location, null, null, ModFlags, false); + for (int i = 0; i < TypeParameters.Length; i++) - if (!TypeParameters [i].Resolve (Parent)) + if (!TypeParameters [i].Resolve (this)) return false; return true; @@ -1149,15 +1441,20 @@ namespace Mono.CSharp { public bool Define (MethodBuilder mb) { - if (!Define ()) - return false; - GenericTypeParameterBuilder[] gen_params; string[] names = MemberName.TypeArguments.GetDeclarations (); gen_params = mb.DefineGenericParameters (names); for (int i = 0; i < TypeParameters.Length; i++) TypeParameters [i].Define (gen_params [i]); + if (!Define ()) + return false; + + for (int i = 0; i < TypeParameters.Length; i++) { + if (!TypeParameters [i].ResolveType (ec)) + return false; + } + return true; } @@ -1185,7 +1482,7 @@ namespace Mono.CSharp { public override MemberCache MemberCache { get { - throw new Exception (); + return null; } } @@ -1204,12 +1501,15 @@ namespace Mono.CSharp { return AttributeTargets.Method | AttributeTargets.ReturnValue; } } + + public override string DocCommentHeader { + get { return "M:"; } + } } public class DefaultValueExpression : Expression { Expression expr; - LocalTemporary temp_storage; public DefaultValueExpression (Expression expr, Location loc) { @@ -1219,12 +1519,11 @@ namespace Mono.CSharp { public override Expression DoResolve (EmitContext ec) { - type = ec.DeclSpace.ResolveType (expr, false, loc); - if (type == null) + TypeExpr texpr = expr.ResolveAsTypeTerminal (ec); + if (texpr == null) return null; - if (type.IsGenericParameter || TypeManager.IsValueType (type)) - temp_storage = new LocalTemporary (ec, type); + type = texpr.Type; eclass = ExprClass.Variable; return this; @@ -1232,7 +1531,9 @@ namespace Mono.CSharp { public override void Emit (EmitContext ec) { - if (temp_storage != null) { + if (type.IsGenericParameter || TypeManager.IsValueType (type)) { + LocalTemporary temp_storage = new LocalTemporary (ec, type); + temp_storage.AddressOf (ec, AddressOp.LoadStore); ec.ig.Emit (OpCodes.Initobj, type); temp_storage.Emit (ec); @@ -1240,4 +1541,1506 @@ namespace Mono.CSharp { ec.ig.Emit (OpCodes.Ldnull); } } + + public class NullableType : TypeExpr + { + Expression underlying; + + public NullableType (Expression underlying, Location l) + { + this.underlying = underlying; + loc = l; + + eclass = ExprClass.Type; + } + + public NullableType (Type type, Location loc) + : this (new TypeExpression (type, loc), loc) + { } + + public override string Name { + get { return underlying.ToString () + "?"; } + } + + public override string FullName { + get { return underlying.ToString () + "?"; } + } + + protected override TypeExpr DoResolveAsTypeStep (EmitContext ec) + { + TypeArguments args = new TypeArguments (loc); + args.Add (underlying); + + ConstructedType ctype = new ConstructedType (TypeManager.generic_nullable_type, args, loc); + return ctype.ResolveAsTypeTerminal (ec); + } + } + + public partial class TypeManager + { + // + // A list of core types that the compiler requires or uses + // + static public Type new_constraint_attr_type; + static public Type activator_type; + static public Type generic_ienumerator_type; + static public Type generic_ienumerable_type; + static public Type generic_nullable_type; + + // + // Tracks the generic parameters. + // + static PtrHashtable builder_to_type_param; + + // + // These methods are called by code generated by the compiler + // + static public MethodInfo activator_create_instance; + + static void InitGenerics () + { + builder_to_type_param = new PtrHashtable (); + } + + static void CleanUpGenerics () + { + builder_to_type_param = null; + } + + static void InitGenericCoreTypes () + { + activator_type = CoreLookupType ("System.Activator"); + new_constraint_attr_type = CoreLookupType ( + "System.Runtime.CompilerServices.NewConstraintAttribute"); + + generic_ienumerator_type = CoreLookupType ("System.Collections.Generic.IEnumerator", 1); + generic_ienumerable_type = CoreLookupType ("System.Collections.Generic.IEnumerable", 1); + generic_nullable_type = CoreLookupType ("System.Nullable", 1); + } + + static void InitGenericCodeHelpers () + { + // Activator + Type [] type_arg = { type_type }; + activator_create_instance = GetMethod ( + activator_type, "CreateInstance", type_arg); + } + + static Type CoreLookupType (string name, int arity) + { + return CoreLookupType (MemberName.MakeName (name, arity)); + } + + public static void AddTypeParameter (Type t, TypeParameter tparam) + { + if (!builder_to_type_param.Contains (t)) + builder_to_type_param.Add (t, tparam); + } + + public static TypeContainer LookupGenericTypeContainer (Type t) + { + while (t.IsGenericInstance) + t = t.GetGenericTypeDefinition (); + + return LookupTypeContainer (t); + } + + public static TypeParameter LookupTypeParameter (Type t) + { + return (TypeParameter) builder_to_type_param [t]; + } + + public static GenericConstraints GetTypeParameterConstraints (Type t) + { + if (!t.IsGenericParameter) + throw new InvalidOperationException (); + + TypeParameter tparam = LookupTypeParameter (t); + if (tparam != null) + return tparam.GenericConstraints; + + return new ReflectionConstraints (t); + } + + public static bool IsGeneric (Type t) + { + DeclSpace ds = (DeclSpace) builder_to_declspace [t]; + + return ds.IsGeneric; + } + + public static bool HasGenericArguments (Type t) + { + return GetNumberOfTypeArguments (t) > 0; + } + + public static int GetNumberOfTypeArguments (Type t) + { + DeclSpace tc = LookupDeclSpace (t); + if (tc != null) + return tc.IsGeneric ? tc.CountTypeParameters : 0; + else + return t.HasGenericArguments ? t.GetGenericArguments ().Length : 0; + } + + public static Type[] GetTypeArguments (Type t) + { + DeclSpace tc = LookupDeclSpace (t); + if (tc != null) { + if (!tc.IsGeneric) + return Type.EmptyTypes; + + TypeParameter[] tparam = tc.TypeParameters; + Type[] ret = new Type [tparam.Length]; + for (int i = 0; i < tparam.Length; i++) { + ret [i] = tparam [i].Type; + if (ret [i] == null) + throw new InternalErrorException (); + } + + return ret; + } else + return t.GetGenericArguments (); + } + + // + // Whether `array' is an array of T and `enumerator' is `IEnumerable'. + // For instance "string[]" -> "IEnumerable". + // + public static bool IsIEnumerable (Type array, Type enumerator) + { + if (!array.IsArray || !enumerator.IsGenericInstance) + return false; + + if (enumerator.GetGenericTypeDefinition () != generic_ienumerable_type) + return false; + + Type[] args = GetTypeArguments (enumerator); + return args [0] == GetElementType (array); + } + + public static bool IsEqual (Type a, Type b) + { + if (a.Equals (b)) + return true; + + if ((a is TypeBuilder) && a.IsGenericTypeDefinition && b.IsGenericInstance) { + // + // `a' is a generic type definition's TypeBuilder and `b' is a + // generic instance of the same type. + // + // Example: + // + // class Stack + // { + // void Test (Stack stack) { } + // } + // + // The first argument of `Test' will be the generic instance + // "Stack" - which is the same type than the "Stack" TypeBuilder. + // + // + // We hit this via Closure.Filter() for gen-82.cs. + // + if (a != b.GetGenericTypeDefinition ()) + return false; + + Type[] aparams = a.GetGenericArguments (); + Type[] bparams = b.GetGenericArguments (); + + if (aparams.Length != bparams.Length) + return false; + + for (int i = 0; i < aparams.Length; i++) + if (!IsEqual (aparams [i], bparams [i])) + return false; + + return true; + } + + if ((b is TypeBuilder) && b.IsGenericTypeDefinition && a.IsGenericInstance) + return IsEqual (b, a); + + if (a.IsGenericParameter && b.IsGenericParameter) { + if ((a.DeclaringMethod == null) || (b.DeclaringMethod == null)) + return false; + return a.GenericParameterPosition == b.GenericParameterPosition; + } + + if (a.IsArray && b.IsArray) { + if (a.GetArrayRank () != b.GetArrayRank ()) + return false; + return IsEqual (a.GetElementType (), b.GetElementType ()); + } + + if (a.IsGenericInstance && b.IsGenericInstance) { + if (a.GetGenericTypeDefinition () != b.GetGenericTypeDefinition ()) + return false; + + Type[] aargs = a.GetGenericArguments (); + Type[] bargs = b.GetGenericArguments (); + + if (aargs.Length != bargs.Length) + return false; + + for (int i = 0; i < aargs.Length; i++) { + if (!IsEqual (aargs [i], bargs [i])) + return false; + } + + return true; + } + + return false; + } + + public static bool MayBecomeEqualGenericTypes (Type a, Type b, Type[] class_infered, Type[] method_infered) + { + if (a.IsGenericParameter) { + // + // If a is an array of a's type, they may never + // become equal. + // + while (b.IsArray) { + b = b.GetElementType (); + if (a.Equals (b)) + return false; + } + + // + // If b is a generic parameter or an actual type, + // they may become equal: + // + // class X : I, I + // class X : I, I + // + if (b.IsGenericParameter || !b.IsGenericInstance) { + int pos = a.GenericParameterPosition; + Type[] args = a.DeclaringMethod != null ? method_infered : class_infered; + if (args [pos] == null) { + args [pos] = b; + return true; + } + + return args [pos] == a; + } + + // + // We're now comparing a type parameter with a + // generic instance. They may become equal unless + // the type parameter appears anywhere in the + // generic instance: + // + // class X : I, I> + // -> error because you could instanciate it as + // X,int> + // + // class X : I, I> -> ok + // + + Type[] bargs = GetTypeArguments (b); + for (int i = 0; i < bargs.Length; i++) { + if (a.Equals (bargs [i])) + return false; + } + + return true; + } + + if (b.IsGenericParameter) + return MayBecomeEqualGenericTypes (b, a, class_infered, method_infered); + + // + // At this point, neither a nor b are a type parameter. + // + // If one of them is a generic instance, let + // MayBecomeEqualGenericInstances() compare them (if the + // other one is not a generic instance, they can never + // become equal). + // + + if (a.IsGenericInstance || b.IsGenericInstance) + return MayBecomeEqualGenericInstances (a, b, class_infered, method_infered); + + // + // If both of them are arrays. + // + + if (a.IsArray && b.IsArray) { + if (a.GetArrayRank () != b.GetArrayRank ()) + return false; + + a = a.GetElementType (); + b = b.GetElementType (); + + return MayBecomeEqualGenericTypes (a, b, class_infered, method_infered); + } + + // + // Ok, two ordinary types. + // + + return a.Equals (b); + } + + // + // Checks whether two generic instances may become equal for some + // particular instantiation (26.3.1). + // + public static bool MayBecomeEqualGenericInstances (Type a, Type b, + Type[] class_infered, Type[] method_infered) + { + if (!a.IsGenericInstance || !b.IsGenericInstance) + return false; + if (a.GetGenericTypeDefinition () != b.GetGenericTypeDefinition ()) + return false; + + return MayBecomeEqualGenericInstances ( + GetTypeArguments (a), GetTypeArguments (b), class_infered, method_infered); + } + + public static bool MayBecomeEqualGenericInstances (Type[] aargs, Type[] bargs, + Type[] class_infered, Type[] method_infered) + { + if (aargs.Length != bargs.Length) + return false; + + for (int i = 0; i < aargs.Length; i++) { + if (!MayBecomeEqualGenericTypes (aargs [i], bargs [i], class_infered, method_infered)) + return false; + } + + return true; + } + + public static bool IsEqualGenericInstance (Type type, Type parent) + { + int tcount = GetNumberOfTypeArguments (type); + int pcount = GetNumberOfTypeArguments (parent); + + if (type.IsGenericInstance) + type = type.GetGenericTypeDefinition (); + if (parent.IsGenericInstance) + parent = parent.GetGenericTypeDefinition (); + + if (tcount != pcount) + return false; + + return type.Equals (parent); + } + + static public bool IsGenericMethod (MethodBase mb) + { + if (mb.DeclaringType is TypeBuilder) { + IMethodData method = (IMethodData) builder_to_method [mb]; + if (method == null) + return false; + + return method.GenericMethod != null; + } + + return mb.IsGenericMethodDefinition; + } + + // + // Type inference. + // + + static bool InferType (Type pt, Type at, Type[] infered) + { + if (pt.IsGenericParameter && (pt.DeclaringMethod != null)) { + int pos = pt.GenericParameterPosition; + + if (infered [pos] == null) { + Type check = at; + while (check.IsArray) + check = check.GetElementType (); + + if (pt == check) + return false; + + infered [pos] = at; + return true; + } + + if (infered [pos] != at) + return false; + + return true; + } + + if (!pt.ContainsGenericParameters) { + if (at.ContainsGenericParameters) + return InferType (at, pt, infered); + else + return true; + } + + if (at.IsArray) { + if (!pt.IsArray || + (at.GetArrayRank () != pt.GetArrayRank ())) + return false; + + return InferType (pt.GetElementType (), at.GetElementType (), infered); + } + + if (pt.IsArray) { + if (!at.IsArray || + (pt.GetArrayRank () != at.GetArrayRank ())) + return false; + + return InferType (pt.GetElementType (), at.GetElementType (), infered); + } + + if (pt.IsByRef && at.IsByRef) + return InferType (pt.GetElementType (), at.GetElementType (), infered); + ArrayList list = new ArrayList (); + if (at.IsGenericInstance) + list.Add (at); + for (Type bt = at.BaseType; bt != null; bt = bt.BaseType) + list.Add (bt); + + list.AddRange (TypeManager.GetInterfaces (at)); + + bool found_one = false; + + foreach (Type type in list) { + if (!type.IsGenericInstance) + continue; + + Type[] infered_types = new Type [infered.Length]; + + if (!InferGenericInstance (pt, type, infered_types)) + continue; + + for (int i = 0; i < infered_types.Length; i++) { + if (infered [i] == null) { + infered [i] = infered_types [i]; + continue; + } + + if (infered [i] != infered_types [i]) + return false; + } + + found_one = true; + } + + return found_one; + } + + static bool InferGenericInstance (Type pt, Type at, Type[] infered_types) + { + Type[] at_args = at.GetGenericArguments (); + Type[] pt_args = pt.GetGenericArguments (); + + if (at_args.Length != pt_args.Length) + return false; + + for (int i = 0; i < at_args.Length; i++) { + if (!InferType (pt_args [i], at_args [i], infered_types)) + return false; + } + + for (int i = 0; i < infered_types.Length; i++) { + if (infered_types [i] == null) + return false; + } + + return true; + } + + public static bool InferParamsTypeArguments (EmitContext ec, ArrayList arguments, + ref MethodBase method) + { + if ((arguments == null) || !TypeManager.IsGenericMethod (method)) + return true; + + int arg_count; + + if (arguments == null) + arg_count = 0; + else + arg_count = arguments.Count; + + ParameterData pd = TypeManager.GetParameterData (method); + + int pd_count = pd.Count; + + if (pd_count == 0) + return false; + + if (pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) + return false; + + if (pd_count - 1 > arg_count) + return false; + + if (pd_count == 1 && arg_count == 0) + return true; + + Type[] method_args = method.GetGenericArguments (); + Type[] infered_types = new Type [method_args.Length]; + + // + // If we have come this far, the case which + // remains is when the number of parameters is + // less than or equal to the argument count. + // + for (int i = 0; i < pd_count - 1; ++i) { + Argument a = (Argument) arguments [i]; + + if ((a.Expr is NullLiteral) || (a.Expr is MethodGroupExpr)) + continue; + + Type pt = pd.ParameterType (i); + Type at = a.Type; + + if (!InferType (pt, at, infered_types)) + return false; + } + + Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1)); + + for (int i = pd_count - 1; i < arg_count; i++) { + Argument a = (Argument) arguments [i]; + + if ((a.Expr is NullLiteral) || (a.Expr is MethodGroupExpr)) + continue; + + if (!InferType (element_type, a.Type, infered_types)) + return false; + } + + for (int i = 0; i < infered_types.Length; i++) + if (infered_types [i] == null) + return false; + + method = method.BindGenericParameters (infered_types); + return true; + } + + public static bool InferTypeArguments (Type[] param_types, Type[] arg_types, Type[] infered_types) + { + if (infered_types == null) + return false; + + for (int i = 0; i < arg_types.Length; i++) { + if (arg_types [i] == null) + continue; + + if (!InferType (param_types [i], arg_types [i], infered_types)) + return false; + } + + for (int i = 0; i < infered_types.Length; i++) + if (infered_types [i] == null) + return false; + + return true; + } + + public static bool InferTypeArguments (EmitContext ec, ArrayList arguments, + ref MethodBase method) + { + if (!TypeManager.IsGenericMethod (method)) + return true; + + int arg_count; + if (arguments != null) + arg_count = arguments.Count; + else + arg_count = 0; + + ParameterData pd = TypeManager.GetParameterData (method); + if (arg_count != pd.Count) + return false; + + Type[] method_args = method.GetGenericArguments (); + + bool is_open = false; + for (int i = 0; i < method_args.Length; i++) { + if (method_args [i].IsGenericParameter) { + is_open = true; + break; + } + } + if (!is_open) + return true; + + Type[] infered_types = new Type [method_args.Length]; + + Type[] param_types = new Type [pd.Count]; + Type[] arg_types = new Type [pd.Count]; + + for (int i = 0; i < arg_count; i++) { + param_types [i] = pd.ParameterType (i); + + Argument a = (Argument) arguments [i]; + if ((a.Expr is NullLiteral) || (a.Expr is MethodGroupExpr) || + (a.Expr is AnonymousMethod)) + continue; + + arg_types [i] = a.Type; + } + + if (!InferTypeArguments (param_types, arg_types, infered_types)) + return false; + + method = method.BindGenericParameters (infered_types); + return true; + } + + public static bool InferTypeArguments (EmitContext ec, ParameterData apd, + ref MethodBase method) + { + if (!TypeManager.IsGenericMethod (method)) + return true; + + ParameterData pd = TypeManager.GetParameterData (method); + if (apd.Count != pd.Count) + return false; + + Type[] method_args = method.GetGenericArguments (); + Type[] infered_types = new Type [method_args.Length]; + + Type[] param_types = new Type [pd.Count]; + Type[] arg_types = new Type [pd.Count]; + + for (int i = 0; i < apd.Count; i++) { + param_types [i] = pd.ParameterType (i); + arg_types [i] = apd.ParameterType (i); + } + + if (!InferTypeArguments (param_types, arg_types, infered_types)) + return false; + + method = method.BindGenericParameters (infered_types); + return true; + } + + public static bool IsNullableType (Type t) + { + if (!t.IsGenericInstance) + return false; + + Type gt = t.GetGenericTypeDefinition (); + return gt == generic_nullable_type; + } + } + + public abstract class Nullable + { + protected sealed class NullableInfo + { + public readonly Type Type; + public readonly Type UnderlyingType; + public readonly MethodInfo HasValue; + public readonly MethodInfo Value; + public readonly ConstructorInfo Constructor; + + public NullableInfo (Type type) + { + Type = type; + UnderlyingType = TypeManager.GetTypeArguments (type) [0]; + + PropertyInfo has_value_pi = type.GetProperty ("HasValue"); + PropertyInfo value_pi = type.GetProperty ("Value"); + + HasValue = has_value_pi.GetGetMethod (false); + Value = value_pi.GetGetMethod (false); + Constructor = type.GetConstructor (new Type[] { UnderlyingType }); + } + } + + protected class Unwrap : Expression, IMemoryLocation, IAssignMethod + { + Expression expr; + NullableInfo info; + + LocalTemporary temp; + bool has_temp; + + public Unwrap (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + if (!(expr is IMemoryLocation)) + temp = new LocalTemporary (ec, expr.Type); + + info = new NullableInfo (expr.Type); + type = info.UnderlyingType; + eclass = expr.eclass; + return this; + } + + public override void Emit (EmitContext ec) + { + AddressOf (ec, AddressOp.LoadStore); + ec.ig.EmitCall (OpCodes.Call, info.Value, null); + } + + public void EmitCheck (EmitContext ec) + { + AddressOf (ec, AddressOp.LoadStore); + ec.ig.EmitCall (OpCodes.Call, info.HasValue, null); + } + + void create_temp (EmitContext ec) + { + if ((temp != null) && !has_temp) { + expr.Emit (ec); + temp.Store (ec); + has_temp = true; + } + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + create_temp (ec); + if (temp != null) + temp.AddressOf (ec, AddressOp.LoadStore); + else + ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + create_temp (ec); + if (leave_copy) { + if (temp != null) + temp.Emit (ec); + else + expr.Emit (ec); + } + + Emit (ec); + } + + public void EmitAssign (EmitContext ec, Expression source, + bool leave_copy, bool prepare_for_load) + { + source.Emit (ec); + ec.ig.Emit (OpCodes.Newobj, info.Constructor); + + if (leave_copy) + ec.ig.Emit (OpCodes.Dup); + + Expression empty = new EmptyExpression (expr.Type); + ((IAssignMethod) expr).EmitAssign (ec, empty, false, prepare_for_load); + } + } + + protected class Wrap : Expression + { + Expression expr; + NullableInfo info; + + public Wrap (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + TypeExpr target_type = new NullableType (expr.Type, loc); + target_type = target_type.ResolveAsTypeTerminal (ec); + if (target_type == null) + return null; + + type = target_type.Type; + info = new NullableInfo (type); + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + expr.Emit (ec); + ec.ig.Emit (OpCodes.Newobj, info.Constructor); + } + } + + public class NullableLiteral : Expression, IMemoryLocation { + public NullableLiteral (Type target_type, Location loc) + { + this.type = target_type; + this.loc = loc; + + eclass = ExprClass.Value; + } + + public override Expression DoResolve (EmitContext ec) + { + return this; + } + + public override void Emit (EmitContext ec) + { + LocalTemporary value_target = new LocalTemporary (ec, type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.ig.Emit (OpCodes.Initobj, type); + value_target.Emit (ec); + } + + public void AddressOf (EmitContext ec, AddressOp Mode) + { + LocalTemporary value_target = new LocalTemporary (ec, type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.ig.Emit (OpCodes.Initobj, type); + ((IMemoryLocation) value_target).AddressOf (ec, Mode); + } + } + + public abstract class Lifted : Expression, IMemoryLocation + { + Expression expr, underlying, wrap, null_value; + Unwrap unwrap; + + protected Lifted (Expression expr, Location loc) + { + this.expr = expr; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + unwrap = (Unwrap) new Unwrap (expr, loc).Resolve (ec); + if (unwrap == null) + return null; + + underlying = ResolveUnderlying (unwrap, ec); + if (underlying == null) + return null; + + wrap = new Wrap (underlying, loc).Resolve (ec); + if (wrap == null) + return null; + + null_value = new NullableLiteral (wrap.Type, loc).Resolve (ec); + if (null_value == null) + return null; + + type = wrap.Type; + eclass = ExprClass.Value; + return this; + } + + protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec); + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + + wrap.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (is_null_label); + null_value.Emit (ec); + + ig.MarkLabel (end_label); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } + } + + public class LiftedConversion : Lifted + { + public readonly bool IsUser; + public readonly bool IsExplicit; + public readonly Type TargetType; + + public LiftedConversion (Expression expr, Type target_type, bool is_user, + bool is_explicit, Location loc) + : base (expr, loc) + { + this.IsUser = is_user; + this.IsExplicit = is_explicit; + this.TargetType = target_type; + } + + protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) + { + Type type = TypeManager.GetTypeArguments (TargetType) [0]; + + if (IsUser) { + return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit); + } else { + if (IsExplicit) + return Convert.ExplicitConversion (ec, unwrap, type, loc); + else + return Convert.ImplicitConversion (ec, unwrap, type, loc); + } + } + } + + public class LiftedUnaryOperator : Lifted + { + public readonly Unary.Operator Oper; + + public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) + : base (expr, loc) + { + this.Oper = op; + } + + protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) + { + return new Unary (Oper, unwrap, loc); + } + } + + public class LiftedConditional : Lifted + { + Expression true_expr, false_expr; + + public LiftedConditional (Expression expr, Expression true_expr, Expression false_expr, + Location loc) + : base (expr, loc) + { + this.true_expr = true_expr; + this.false_expr = false_expr; + } + + protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) + { + return new Conditional (unwrap, true_expr, false_expr, loc); + } + } + + public class LiftedBinaryOperator : Expression + { + public readonly Binary.Operator Oper; + + Expression left, right, underlying, null_value, bool_wrap; + Unwrap left_unwrap, right_unwrap; + bool is_equality, is_comparision, is_boolean; + + public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right, + Location loc) + { + this.Oper = op; + this.left = left; + this.right = right; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + if (TypeManager.IsNullableType (left.Type)) { + left_unwrap = new Unwrap (left, loc); + left = left_unwrap.Resolve (ec); + if (left == null) + return null; + } + + if (TypeManager.IsNullableType (right.Type)) { + right_unwrap = new Unwrap (right, loc); + right = right_unwrap.Resolve (ec); + if (right == null) + return null; + } + + if (((Oper == Binary.Operator.BitwiseAnd) || (Oper == Binary.Operator.BitwiseOr) || + (Oper == Binary.Operator.LogicalAnd) || (Oper == Binary.Operator.LogicalOr)) && + ((left.Type == TypeManager.bool_type) && (right.Type == TypeManager.bool_type))) { + Expression empty = new EmptyExpression (TypeManager.bool_type); + bool_wrap = new Wrap (empty, loc).Resolve (ec); + null_value = new NullableLiteral (bool_wrap.Type, loc).Resolve (ec); + + type = bool_wrap.Type; + is_boolean = true; + } else if ((Oper == Binary.Operator.Equality) || (Oper == Binary.Operator.Inequality)) { + if (!(left is NullLiteral) && !(right is NullLiteral)) { + underlying = new Binary (Oper, left, right, loc).Resolve (ec); + if (underlying == null) + return null; + } + + type = TypeManager.bool_type; + is_equality = true; + } else if ((Oper == Binary.Operator.LessThan) || + (Oper == Binary.Operator.GreaterThan) || + (Oper == Binary.Operator.LessThanOrEqual) || + (Oper == Binary.Operator.GreaterThanOrEqual)) { + underlying = new Binary (Oper, left, right, loc).Resolve (ec); + if (underlying == null) + return null; + + type = TypeManager.bool_type; + is_comparision = true; + } else { + underlying = new Binary (Oper, left, right, loc).Resolve (ec); + if (underlying == null) + return null; + + underlying = new Wrap (underlying, loc).Resolve (ec); + if (underlying == null) + return null; + + type = underlying.Type; + null_value = new NullableLiteral (type, loc).Resolve (ec); + } + + eclass = ExprClass.Value; + return this; + } + + void EmitBoolean (EmitContext ec) + { + ILGenerator ig = ec.ig; + + Label left_is_null_label = ig.DefineLabel (); + Label right_is_null_label = ig.DefineLabel (); + Label is_null_label = ig.DefineLabel (); + Label wrap_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + if (left_unwrap != null) { + left_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, left_is_null_label); + } + + left.Emit (ec); + ig.Emit (OpCodes.Dup); + if ((Oper == Binary.Operator.BitwiseOr) || (Oper == Binary.Operator.LogicalOr)) + ig.Emit (OpCodes.Brtrue, wrap_label); + else + ig.Emit (OpCodes.Brfalse, wrap_label); + + if (right_unwrap != null) { + right_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, right_is_null_label); + } + + if ((Oper == Binary.Operator.LogicalAnd) || (Oper == Binary.Operator.LogicalOr)) + ig.Emit (OpCodes.Pop); + + right.Emit (ec); + if (Oper == Binary.Operator.BitwiseOr) + ig.Emit (OpCodes.Or); + else if (Oper == Binary.Operator.BitwiseAnd) + ig.Emit (OpCodes.And); + ig.Emit (OpCodes.Br, wrap_label); + + ig.MarkLabel (left_is_null_label); + if (right_unwrap != null) { + right_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } + + right.Emit (ec); + ig.Emit (OpCodes.Dup); + if ((Oper == Binary.Operator.BitwiseOr) || (Oper == Binary.Operator.LogicalOr)) + ig.Emit (OpCodes.Brtrue, wrap_label); + else + ig.Emit (OpCodes.Brfalse, wrap_label); + + ig.MarkLabel (right_is_null_label); + ig.Emit (OpCodes.Pop); + ig.MarkLabel (is_null_label); + null_value.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (wrap_label); + ig.Emit (OpCodes.Nop); + bool_wrap.Emit (ec); + ig.Emit (OpCodes.Nop); + + ig.MarkLabel (end_label); + } + + void EmitEquality (EmitContext ec) + { + ILGenerator ig = ec.ig; + + Label left_not_null_label = ig.DefineLabel (); + Label false_label = ig.DefineLabel (); + Label true_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + if (left_unwrap != null) { + left_unwrap.EmitCheck (ec); + if (right is NullLiteral) { + if (Oper == Binary.Operator.Equality) + ig.Emit (OpCodes.Brfalse, true_label); + else + ig.Emit (OpCodes.Brfalse, false_label); + } else if (right_unwrap != null) { + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Brtrue, left_not_null_label); + right_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Ceq); + if (Oper == Binary.Operator.Inequality) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); + } + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (left_not_null_label); + ig.Emit (OpCodes.Pop); + } else { + if (Oper == Binary.Operator.Equality) + ig.Emit (OpCodes.Brfalse, false_label); + else + ig.Emit (OpCodes.Brfalse, true_label); + } + } + + if (right_unwrap != null) { + right_unwrap.EmitCheck (ec); + if (left is NullLiteral) { + if (Oper == Binary.Operator.Equality) + ig.Emit (OpCodes.Brfalse, true_label); + else + ig.Emit (OpCodes.Brfalse, false_label); + } else { + if (Oper == Binary.Operator.Equality) + ig.Emit (OpCodes.Brfalse, false_label); + else + ig.Emit (OpCodes.Brfalse, true_label); + } + } + + bool left_is_null = left is NullLiteral; + bool right_is_null = right is NullLiteral; + if (left_is_null || right_is_null) { + if (((Oper == Binary.Operator.Equality) && (left_is_null == right_is_null)) || + ((Oper == Binary.Operator.Inequality) && (left_is_null != right_is_null))) + ig.Emit (OpCodes.Br, true_label); + else + ig.Emit (OpCodes.Br, false_label); + } else { + underlying.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + } + + ig.MarkLabel (false_label); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (true_label); + ig.Emit (OpCodes.Ldc_I4_1); + + ig.MarkLabel (end_label); + } + + void EmitComparision (EmitContext ec) + { + ILGenerator ig = ec.ig; + + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + if (left_unwrap != null) { + left_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } + + if (right_unwrap != null) { + right_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } + + underlying.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (is_null_label); + ig.Emit (OpCodes.Ldc_I4_0); + + ig.MarkLabel (end_label); + } + + public override void Emit (EmitContext ec) + { + if (is_boolean) { + EmitBoolean (ec); + return; + } else if (is_equality) { + EmitEquality (ec); + return; + } else if (is_comparision) { + EmitComparision (ec); + return; + } + + ILGenerator ig = ec.ig; + + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + if (left_unwrap != null) { + left_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } + + if (right_unwrap != null) { + right_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } + + underlying.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (is_null_label); + null_value.Emit (ec); + + ig.MarkLabel (end_label); + } + } + + public class OperatorTrueOrFalse : Expression + { + public readonly bool IsTrue; + + Expression expr; + Unwrap unwrap; + + public OperatorTrueOrFalse (Expression expr, bool is_true, Location loc) + { + this.IsTrue = is_true; + this.expr = expr; + this.loc = loc; + } + + public override Expression DoResolve (EmitContext ec) + { + unwrap = new Unwrap (expr, loc); + expr = unwrap.Resolve (ec); + if (expr == null) + return null; + + if (unwrap.Type != TypeManager.bool_type) + return null; + + type = TypeManager.bool_type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + + unwrap.Emit (ec); + if (!IsTrue) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); + } + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (is_null_label); + ig.Emit (OpCodes.Ldc_I4_0); + + ig.MarkLabel (end_label); + } + } + + public class NullCoalescingOperator : Expression + { + Expression left, right; + Expression expr; + Unwrap unwrap; + + public NullCoalescingOperator (Expression left, Expression right, Location loc) + { + this.left = left; + this.right = right; + this.loc = loc; + + eclass = ExprClass.Value; + } + + public override Expression DoResolve (EmitContext ec) + { + if (type != null) + return this; + + left = left.Resolve (ec); + if (left == null) + return null; + + right = right.Resolve (ec); + if (right == null) + return null; + + Type ltype = left.Type, rtype = right.Type; + + if (!TypeManager.IsNullableType (ltype) && ltype.IsValueType) { + Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); + return null; + } + + if (TypeManager.IsNullableType (ltype)) { + NullableInfo info = new NullableInfo (ltype); + + unwrap = (Unwrap) new Unwrap (left, loc).Resolve (ec); + if (unwrap == null) + return null; + + expr = Convert.ImplicitConversion (ec, right, info.UnderlyingType, loc); + if (expr != null) { + left = unwrap; + type = expr.Type; + return this; + } + } + + expr = Convert.ImplicitConversion (ec, right, ltype, loc); + if (expr != null) { + type = expr.Type; + return this; + } + + if (unwrap != null) { + expr = Convert.ImplicitConversion (ec, unwrap, rtype, loc); + if (expr != null) { + left = expr; + expr = right; + type = expr.Type; + return this; + } + } + + Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); + return null; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + if (unwrap != null) { + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + + left.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (is_null_label); + expr.Emit (ec); + + ig.MarkLabel (end_label); + } else { + left.Emit (ec); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Brtrue, end_label); + + ig.MarkLabel (is_null_label); + + ig.Emit (OpCodes.Pop); + expr.Emit (ec); + + ig.MarkLabel (end_label); + } + } + } + + public class LiftedUnaryMutator : ExpressionStatement + { + public readonly UnaryMutator.Mode Mode; + Expression expr, null_value; + UnaryMutator underlying; + Unwrap unwrap; + + public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc) + { + this.expr = expr; + this.Mode = mode; + this.loc = loc; + + eclass = ExprClass.Value; + } + + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + unwrap = (Unwrap) new Unwrap (expr, loc).Resolve (ec); + if (unwrap == null) + return null; + + underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec); + if (underlying == null) + return null; + + null_value = new NullableLiteral (expr.Type, loc).Resolve (ec); + if (null_value == null) + return null; + + type = expr.Type; + return this; + } + + void DoEmit (EmitContext ec, bool is_expr) + { + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + + if (is_expr) + underlying.Emit (ec); + else + underlying.EmitStatement (ec); + ig.Emit (OpCodes.Br, end_label); + + ig.MarkLabel (is_null_label); + if (is_expr) + null_value.Emit (ec); + + ig.MarkLabel (end_label); + } + + public override void Emit (EmitContext ec) + { + DoEmit (ec, true); + } + + public override void EmitStatement (EmitContext ec) + { + DoEmit (ec, false); + } + } + } }