--- /dev/null
+//
+// tuples.cs: Tuples types
+//
+// Author:
+// Marek Safar (marek.safar@gmail.com)
+//
+// Dual licensed under the terms of the MIT X11 or GNU GPL
+//
+// Copyright (C) Microsoft Corporation.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
+
+#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
+{
+ class TupleTypeExpr : TypeExpr
+ {
+ TypeArguments elements;
+ List<string> names;
+
+ public TupleTypeExpr (TypeArguments elements, List<string> names, Location loc)
+ {
+ this.elements = elements;
+ this.names = names;
+ this.loc = loc;
+ }
+
+ public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
+ {
+ var length = elements.Count;
+ if (length > 7)
+ throw new NotImplementedException ("tuples > 7");
+
+ eclass = ExprClass.Type;
+
+ var otype = mc.Module.PredefinedTypes.Tuples [length - 1].Resolve ();
+ if (otype == null)
+ return null;
+
+ GenericTypeExpr ctype = new GenericTypeExpr (otype, elements, loc);
+ type = ctype.ResolveAsType (mc);
+
+ if (names != null && CheckElementNames (mc) && type != null) {
+ type = NamedTupleSpec.MakeType (mc.Module, (InflatedTypeSpec) type, names);
+ }
+
+ return type;
+ }
+
+ bool CheckElementNames (IMemberContext mc)
+ {
+ int first_name = -1;
+ for (int i = 0; i < names.Count; ++i) {
+ var name = names [i];
+ if (name == null)
+ continue;
+
+ if (IsReservedName (name)) {
+ mc.Module.Compiler.Report.Error (8126, loc, "The tuple element name `{0}' is reserved", name);
+ names [i] = null;
+ continue;
+ }
+
+ if (name.StartsWith ("Item", StringComparison.Ordinal)) {
+ var idx = name.Substring (4);
+ uint value;
+ if (uint.TryParse (idx, NumberStyles.Integer, CultureInfo.InvariantCulture, out value) && value != i + 1) {
+ mc.Module.Compiler.Report.Error (8125, loc, "The tuple element name `{0}' can only be used at position {1}", name, idx);
+ names [i] = null;
+ continue;
+ }
+ }
+
+ if (first_name < 0) {
+ first_name = i;
+ continue;
+ }
+
+ for (int ii = first_name; ii < i; ++ii) {
+ if (name == names [ii]) {
+ mc.Module.Compiler.Report.Error (8127, loc, "The tuple element name `{0}' is a duplicate", name);
+ names [i] = null;
+ break;
+ }
+ }
+ }
+
+ return first_name >= 0;
+ }
+
+ static bool IsReservedName (string name)
+ {
+ switch (name) {
+ case "CompareTo":
+ case "Deconstruct":
+ case "Equals":
+ case "GetHashCode":
+ case "Rest":
+ case "ToString":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public override string GetSignatureForError ()
+ {
+ var sb = new StringBuilder ();
+ for (int i = 0; i < elements.Count; ++i) {
+ sb.Append (elements.Arguments [i].GetSignatureForError ());
+
+ if (names [i] != null) {
+ sb.Append (" ");
+ sb.Append (names [i]);
+ }
+
+ if (i != 0)
+ sb.Append (",");
+ }
+
+ return sb.ToString ();
+ }
+ }
+
+ public class NamedTupleSpec : TypeSpec
+ {
+ InflatedTypeSpec tuple;
+ IList<string> elements;
+
+ private NamedTupleSpec (InflatedTypeSpec tupleDefinition, IList<string> elements)
+ : base (tupleDefinition.Kind, tupleDefinition.DeclaringType, tupleDefinition.MemberDefinition, null, tupleDefinition.Modifiers)
+ {
+ tuple = tupleDefinition;
+ this.elements = elements;
+
+ state |= StateFlags.HasNamedTupleElement | StateFlags.Tuple;
+ }
+
+ public IList<string> Elements {
+ get {
+ return elements;
+ }
+ }
+
+ public override TypeSpec [] TypeArguments {
+ get {
+ return tuple.TypeArguments;
+ }
+ }
+
+ protected override void InitializeMemberCache (bool onlyTypes)
+ {
+ cache = tuple.MemberCache;
+ }
+
+ public override MetaType GetMetaInfo ()
+ {
+ return tuple.GetMetaInfo ();
+ }
+
+ public MemberSpec FindElement (IMemberContext mc, string name, Location loc)
+ {
+ // TODO: cache it
+ for (int i = 0; i < elements.Count; ++i) {
+ var ename = elements [i];
+ if (ename == null || ename != name)
+ continue;
+
+ var member_name = GetElementPropertyName (i);
+ var ms = MemberCache.FindMember (tuple, MemberFilter.Field (member_name, null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly);
+ if (ms == null) {
+ mc.Module.Compiler.Report.Error (8128, loc, "Member `{0}' was not found on type '{1}'", member_name, tuple.GetSignatureForError ());
+ return null;
+ }
+
+ return ms;
+ }
+
+ return null;
+ }
+
+ public override string GetSignatureForError ()
+ {
+ //
+ // csc reports names as well but it seems to me redundant when
+ // they are not included in any type conversion
+ //
+ return tuple.GetSignatureForError ();
+ }
+
+ public string GetSignatureForErrorWithNames ()
+ {
+ // TODO: Include names
+ return tuple.GetSignatureForError ();
+ }
+
+ public static NamedTupleSpec MakeType (ModuleContainer module, InflatedTypeSpec tupleType, IList<string> names)
+ {
+ // TODO: cache it
+ return new NamedTupleSpec (tupleType, names);
+ }
+
+ public static string GetElementPropertyName (int index)
+ {
+ return "Item" + (index + 1).ToString (CultureInfo.InvariantCulture);
+ }
+
+ public static bool CheckOverrideName (IParametersMember member, IParametersMember baseMember)
+ {
+ var btypes = baseMember.Parameters.Types;
+ var ttypes = member.Parameters.Types;
+ for (int ii = 0; ii < baseMember.Parameters.Count; ++ii) {
+ if (!CheckOverrideName (ttypes [ii], btypes [ii])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static bool CheckOverrideName (TypeSpec type, TypeSpec baseType)
+ {
+ var btype_ntuple = baseType as NamedTupleSpec;
+ var mtype_ntupe = type as NamedTupleSpec;
+ if (btype_ntuple == null && mtype_ntupe == null)
+ return true;
+
+ if (btype_ntuple != null || mtype_ntupe != null)
+ return false;
+
+ var b_elements = btype_ntuple.elements;
+ var m_elements = mtype_ntupe.elements;
+ for (int i = 0; i < b_elements.Count; ++i) {
+ if (b_elements [i] != m_elements [i])
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ class TupleLiteralElement
+ {
+ public TupleLiteralElement (string name, Expression expr, Location loc)
+ {
+ this.Name = name;
+ this.Expr = expr;
+ this.Location = loc;
+ }
+
+ public TupleLiteralElement (Expression expr)
+ {
+ this.Expr = expr;
+ this.Location = expr.Location;
+ }
+
+ public string Name { get; private set; }
+ public Expression Expr { get; set; }
+ public Location Location { get; private set; }
+ }
+
+ sealed class TupleLiteral : Expression
+ {
+ List<TupleLiteralElement> elements;
+
+ public TupleLiteral (List<TupleLiteralElement> elements, Location loc)
+ {
+ this.elements = elements;
+ this.loc = loc;
+ }
+
+ public List<TupleLiteralElement> Elements {
+ get {
+ return elements;
+ }
+ }
+
+ public static bool ContainsNoTypeElement (TypeSpec type)
+ {
+ var ta = type.TypeArguments;
+
+ for (int i = 0; i < ta.Length; ++i) {
+ var et = ta [i];
+ if (InternalType.HasNoType (et))
+ return true;
+
+ if (et.IsTupleType && ContainsNoTypeElement (et))
+ return true;
+ }
+
+ return false;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext rc)
+ {
+ rc.Report.Error (8143, loc, "An expression tree cannot contain a tuple literal");
+ return ErrorExpression.Instance;
+ }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ var ta = new TypeArguments ();
+ List<string> names = null;
+
+ for (int i = 0; i < elements.Count; ++i) {
+ var el = elements [i];
+ var expr = el.Expr.Resolve (rc);
+ if (expr == null) {
+ el.Expr = null;
+ ta = null;
+ continue;
+ }
+
+ if (expr.Type.Kind == MemberKind.Void) {
+ rc.Report.Error (8210, expr.Location, "A tuple literal cannot not contain a value of type `{0}'", expr.Type.GetSignatureForError ());
+ expr = null;
+ ta = null;
+ continue;
+ }
+
+ if (el.Name != null) {
+ if (names == null) {
+ names = new List<string> ();
+ for (int ii = 0; ii < i; ++ii) {
+ names.Add (null);
+ }
+ }
+
+ names.Add (el.Name);
+ }
+
+ el.Expr = expr;
+
+ if (ta != null)
+ ta.Add (new TypeExpression (expr.Type, expr.Location));
+ }
+
+ eclass = ExprClass.Value;
+
+ if (ta == null)
+ return null;
+
+ var t = new TupleTypeExpr (ta, names, loc);
+ type = t.ResolveAsType (rc) ?? InternalType.ErrorType;
+
+ return this;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ foreach (var el in elements) {
+ el.Expr.Emit (ec);
+ }
+
+ // TODO: Needs arguments check
+ var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec;
+
+ ec.Emit (OpCodes.Newobj, ctor);
+ }
+
+ public override void Error_ValueCannotBeConverted (ResolveContext rc, TypeSpec target, bool expl)
+ {
+ rc.Report.Error (8135, Location, "Tuple literal `{0}' cannot be converted to type `{1}'", type.GetSignatureForError (), target.GetSignatureForError ());
+ }
+ }
+
+ //
+ // Used when converting from a tuple literal or tuple instance to different tuple type
+ //
+ class TupleLiteralConversion : Expression
+ {
+ List<Expression> elements;
+ Expression source;
+
+ public TupleLiteralConversion (Expression source, TypeSpec type, List<Expression> elements, Location loc)
+ {
+ this.source = source;
+ this.type = type;
+ this.elements = elements;
+ this.loc = loc;
+
+ eclass = source.eclass;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext rc)
+ {
+ rc.Report.Error (8144, loc, "An expression tree cannot contain a tuple conversion");
+ return null;
+ }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ // Should not be reached
+ throw new NotSupportedException ();
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ if (!(source is TupleLiteral)) {
+ var assign = source as CompilerAssign;
+ if (assign != null)
+ assign.EmitStatement (ec);
+ else
+ source.Emit (ec);
+ }
+
+ foreach (var el in elements) {
+ el.Emit (ec);
+ }
+
+ // TODO: Needs arguments check
+ var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec;
+
+ ec.Emit (OpCodes.Newobj, ctor);
+ }
+ }
+
+ class TupleDeconstruct : ExpressionStatement
+ {
+ Expression source;
+ List<Expression> targetExprs;
+
+ public TupleDeconstruct (List<Expression> targetExprs, Expression source, Location loc)
+ {
+ this.source = source;
+ this.targetExprs = targetExprs;
+ this.loc = loc;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext ec)
+ {
+ ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
+
+ throw new NotImplementedException ();
+ }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ var src = source.Resolve (rc);
+ if (src == null)
+ return null;
+
+ if (InternalType.HasNoType (src.Type)) {
+ rc.Report.Error (8131, source.Location, "Deconstruct assignment requires an expression with a type on the right-hand-side");
+ return null;
+ }
+
+ var tupleLiteral = src as TupleLiteral;
+ if (tupleLiteral != null) {
+ if (tupleLiteral.Elements.Count != targetExprs.Count) {
+ rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{0}' variables",
+ tupleLiteral.Elements.Count.ToString (), targetExprs.Count.ToString ());
+ return null;
+ }
+
+ for (int i = 0; i < targetExprs.Count; ++i) {
+ targetExprs [i] = new SimpleAssign (targetExprs [i], tupleLiteral.Elements [i].Expr).Resolve (rc);
+ }
+
+ eclass = ExprClass.Value;
+ type = src.Type;
+ return this;
+ }
+
+ if (src.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
+ rc.Report.Error (8133, loc, "Cannot deconstruct dynamic objects");
+ return null;
+ }
+
+ throw new NotImplementedException ("Deconstruct assignment");
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override void EmitStatement (EmitContext ec)
+ {
+ foreach (ExpressionStatement expr in targetExprs)
+ expr.EmitStatement (ec);
+ }
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var expr in targetExprs)
+ expr.FlowAnalysis (fc);
+ }
+ }
+}
\ No newline at end of file