From 2ceb6e94027c28dd84a8db68680134a14ec26635 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Fri, 30 Jun 2017 20:22:27 +0200 Subject: [PATCH] [mcs] Adds missing file --- mcs/mcs/tuples.cs | 503 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 mcs/mcs/tuples.cs diff --git a/mcs/mcs/tuples.cs b/mcs/mcs/tuples.cs new file mode 100644 index 00000000000..e4f56ca131b --- /dev/null +++ b/mcs/mcs/tuples.cs @@ -0,0 +1,503 @@ +// +// 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 names; + + public TupleTypeExpr (TypeArguments elements, List 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 elements; + + private NamedTupleSpec (InflatedTypeSpec tupleDefinition, IList elements) + : base (tupleDefinition.Kind, tupleDefinition.DeclaringType, tupleDefinition.MemberDefinition, null, tupleDefinition.Modifiers) + { + tuple = tupleDefinition; + this.elements = elements; + + state |= StateFlags.HasNamedTupleElement | StateFlags.Tuple; + } + + public IList 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 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 elements; + + public TupleLiteral (List elements, Location loc) + { + this.elements = elements; + this.loc = loc; + } + + public List 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 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 (); + 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 elements; + Expression source; + + public TupleLiteralConversion (Expression source, TypeSpec type, List 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 targetExprs; + + public TupleDeconstruct (List 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 -- 2.25.1