/// <summary>
		/// Gets whether the type supports collection initializers.
		/// </summary>
		static bool IsCollectionType(TypeReference tr)
		{
			if (tr == null)
				return false;
			TypeDefinition td = tr.Resolve();
			while (td != null) {
				if (td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
					return true;
				td = td.BaseType != null ? td.BaseType.Resolve() : null;
			}
			return false;
		}
		Expression MakeDefaultValue(TypeReference type)
		{
			TypeDefinition typeDef = type.Resolve();
			if (typeDef != null) {
				if (TypeAnalysis.IsIntegerOrEnum(typeDef))
					return AstBuilder.MakePrimitive(0, typeDef);
				else if (!typeDef.IsValueType)
					return new NullReferenceExpression();
				switch (typeDef.FullName) {
					case "System.Nullable`1":
						return new NullReferenceExpression();
					case "System.Single":
						return new PrimitiveExpression(0f);
					case "System.Double":
						return new PrimitiveExpression(0.0);
					case "System.Decimal":
						return new PrimitiveExpression(0m);
				}
			}
			return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
		}
		static bool OperandFitsInType(TypeReference type, int num)
		{
			TypeDefinition typeDef = type.Resolve() as TypeDefinition;
			if (typeDef != null && typeDef.IsEnum) {
				type = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType;
			}
			switch (type.MetadataType) {
				case MetadataType.SByte:
					return sbyte.MinValue <= num && num <= sbyte.MaxValue;
				case MetadataType.Int16:
					return short.MinValue <= num && num <= short.MaxValue;
				case MetadataType.Byte:
					return byte.MinValue <= num && num <= byte.MaxValue;
				case MetadataType.Char:
					return char.MinValue <= num && num <= char.MaxValue;
				case MetadataType.UInt16:
					return ushort.MinValue <= num && num <= ushort.MaxValue;
				default:
					return true;
			}
		}
		static bool? IsSigned(TypeReference type)
		{
			if (type == null || IsArrayPointerOrReference(type))
				return null;
			// unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
			TypeDefinition typeDef = type.Resolve() as TypeDefinition;
			if (typeDef != null && typeDef.IsEnum) {
				TypeReference underlyingType = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType;
				return IsSigned(underlyingType);
			}
			switch (type.MetadataType) {
				case MetadataType.SByte:
				case MetadataType.Int16:
				case MetadataType.Int32:
				case MetadataType.Int64:
				case MetadataType.IntPtr:
					return true;
				case MetadataType.Byte:
				case MetadataType.Char:
				case MetadataType.UInt16:
				case MetadataType.UInt32:
				case MetadataType.UInt64:
				case MetadataType.UIntPtr:
					return false;
				default:
					return null;
			}
		}
		public static bool IsEnum(TypeReference type)
		{
			// Arrays/Pointers/ByReference resolve to their element type, but we don't want to consider those to be enums
			// However, GenericInstanceTypes, ModOpts etc. should be considered enums.
			if (type == null || IsArrayPointerOrReference(type))
				return false;
			// unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
			TypeDefinition typeDef = type.Resolve() as TypeDefinition;
			return typeDef != null && typeDef.IsEnum;
		}
		public const int NativeInt = 33; // treat native int as between int32 and int64
		
		public static int GetInformationAmount(TypeReference type)
		{
			if (type == null)
				return 0;
			if (type.IsValueType && !IsArrayPointerOrReference(type)) {
				// value type might be an enum
				TypeDefinition typeDef = type.Resolve() as TypeDefinition;
				if (typeDef != null && typeDef.IsEnum) {
					TypeReference underlyingType = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType;
					return GetInformationAmount(underlyingType);
				}
			}
			switch (type.MetadataType) {
				case MetadataType.Void:
					return 0;
				case MetadataType.Boolean:
					return 1;
				case MetadataType.SByte:
				case MetadataType.Byte:
					return 8;
				case MetadataType.Char:
				case MetadataType.Int16:
				case MetadataType.UInt16:
					return 16;
				case MetadataType.Int32:
				case MetadataType.UInt32:
				case MetadataType.Single:
					return 32;
				case MetadataType.Int64:
				case MetadataType.UInt64:
				case MetadataType.Double:
					return 64;
				case MetadataType.IntPtr:
				case MetadataType.UIntPtr:
					return NativeInt;
				default:
					return 100; // we consider structs/objects to have more information than any primitives
			}
		}