2 // tuples.cs: Tuples types
5 // Marek Safar (marek.safar@gmail.com)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright (C) Microsoft Corporation.
13 using System.Collections.Generic;
15 using System.Globalization;
18 using MetaType = IKVM.Reflection.Type;
19 using IKVM.Reflection;
20 using IKVM.Reflection.Emit;
22 using MetaType = System.Type;
23 using System.Reflection;
24 using System.Reflection.Emit;
29 class TupleTypeExpr : TypeExpr
31 TypeArguments elements;
34 public TupleTypeExpr (TypeArguments elements, List<string> names, Location loc)
36 this.elements = elements;
41 public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
43 var length = elements.Count;
45 throw new NotImplementedException ("tuples > 7");
47 eclass = ExprClass.Type;
49 var otype = mc.Module.PredefinedTypes.Tuples [length - 1].Resolve ();
53 GenericTypeExpr ctype = new GenericTypeExpr (otype, elements, loc);
54 type = ctype.ResolveAsType (mc);
56 if (names != null && CheckElementNames (mc) && type != null) {
57 type = NamedTupleSpec.MakeType (mc.Module, (InflatedTypeSpec) type, names);
63 bool CheckElementNames (IMemberContext mc)
66 for (int i = 0; i < names.Count; ++i) {
71 if (IsReservedName (name)) {
72 mc.Module.Compiler.Report.Error (8126, loc, "The tuple element name `{0}' is reserved", name);
77 if (name.StartsWith ("Item", StringComparison.Ordinal)) {
78 var idx = name.Substring (4);
80 if (uint.TryParse (idx, NumberStyles.Integer, CultureInfo.InvariantCulture, out value) && value != i + 1) {
81 mc.Module.Compiler.Report.Error (8125, loc, "The tuple element name `{0}' can only be used at position {1}", name, idx);
92 for (int ii = first_name; ii < i; ++ii) {
93 if (name == names [ii]) {
94 mc.Module.Compiler.Report.Error (8127, loc, "The tuple element name `{0}' is a duplicate", name);
101 return first_name >= 0;
104 static bool IsReservedName (string name)
119 public override string GetSignatureForError ()
121 var sb = new StringBuilder ();
122 for (int i = 0; i < elements.Count; ++i) {
123 sb.Append (elements.Arguments [i].GetSignatureForError ());
125 if (names [i] != null) {
127 sb.Append (names [i]);
134 return sb.ToString ();
138 public class NamedTupleSpec : TypeSpec
140 InflatedTypeSpec tuple;
141 IList<string> elements;
143 private NamedTupleSpec (InflatedTypeSpec tupleDefinition, IList<string> elements)
144 : base (tupleDefinition.Kind, tupleDefinition.DeclaringType, tupleDefinition.MemberDefinition, null, tupleDefinition.Modifiers)
146 tuple = tupleDefinition;
147 this.elements = elements;
149 state |= StateFlags.HasNamedTupleElement | StateFlags.Tuple;
152 public IList<string> Elements {
158 public override TypeSpec [] TypeArguments {
160 return tuple.TypeArguments;
164 protected override void InitializeMemberCache (bool onlyTypes)
166 cache = tuple.MemberCache;
169 public override MetaType GetMetaInfo ()
171 return tuple.GetMetaInfo ();
174 public MemberSpec FindElement (IMemberContext mc, string name, Location loc)
177 for (int i = 0; i < elements.Count; ++i) {
178 var ename = elements [i];
179 if (ename == null || ename != name)
182 var member_name = GetElementPropertyName (i);
183 var ms = MemberCache.FindMember (tuple, MemberFilter.Field (member_name, null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly);
185 mc.Module.Compiler.Report.Error (8128, loc, "Member `{0}' was not found on type '{1}'", member_name, tuple.GetSignatureForError ());
195 public override string GetSignatureForError ()
198 // csc reports names as well but it seems to me redundant when
199 // they are not included in any type conversion
201 return tuple.GetSignatureForError ();
204 public string GetSignatureForErrorWithNames ()
206 // TODO: Include names
207 return tuple.GetSignatureForError ();
210 public static NamedTupleSpec MakeType (ModuleContainer module, InflatedTypeSpec tupleType, IList<string> names)
213 return new NamedTupleSpec (tupleType, names);
216 public static string GetElementPropertyName (int index)
218 return "Item" + (index + 1).ToString (CultureInfo.InvariantCulture);
221 public static bool CheckOverrideName (IParametersMember member, IParametersMember baseMember)
223 var btypes = baseMember.Parameters.Types;
224 var ttypes = member.Parameters.Types;
225 for (int ii = 0; ii < baseMember.Parameters.Count; ++ii) {
226 if (!CheckOverrideName (ttypes [ii], btypes [ii])) {
234 public static bool CheckOverrideName (TypeSpec type, TypeSpec baseType)
236 var btype_ntuple = baseType as NamedTupleSpec;
237 var mtype_ntupe = type as NamedTupleSpec;
238 if (btype_ntuple == null && mtype_ntupe == null)
241 if (btype_ntuple != null || mtype_ntupe != null)
244 var b_elements = btype_ntuple.elements;
245 var m_elements = mtype_ntupe.elements;
246 for (int i = 0; i < b_elements.Count; ++i) {
247 if (b_elements [i] != m_elements [i])
255 class TupleLiteralElement
257 public TupleLiteralElement (string name, Expression expr, Location loc)
264 public TupleLiteralElement (Expression expr)
267 this.Location = expr.Location;
270 public string Name { get; private set; }
271 public Expression Expr { get; set; }
272 public Location Location { get; private set; }
275 sealed class TupleLiteral : Expression
277 List<TupleLiteralElement> elements;
279 public TupleLiteral (List<TupleLiteralElement> elements, Location loc)
281 this.elements = elements;
285 public List<TupleLiteralElement> Elements {
291 public static bool ContainsNoTypeElement (TypeSpec type)
293 var ta = type.TypeArguments;
295 for (int i = 0; i < ta.Length; ++i) {
297 if (InternalType.HasNoType (et))
300 if (et.IsTupleType && ContainsNoTypeElement (et))
307 public override Expression CreateExpressionTree (ResolveContext rc)
309 rc.Report.Error (8143, loc, "An expression tree cannot contain a tuple literal");
310 return ErrorExpression.Instance;
313 protected override Expression DoResolve (ResolveContext rc)
315 var ta = new TypeArguments ();
316 List<string> names = null;
318 for (int i = 0; i < elements.Count; ++i) {
319 var el = elements [i];
320 var expr = el.Expr.Resolve (rc);
327 if (expr.Type.Kind == MemberKind.Void) {
328 rc.Report.Error (8210, expr.Location, "A tuple literal cannot not contain a value of type `{0}'", expr.Type.GetSignatureForError ());
334 if (el.Name != null) {
336 names = new List<string> ();
337 for (int ii = 0; ii < i; ++ii) {
348 ta.Add (new TypeExpression (expr.Type, expr.Location));
351 eclass = ExprClass.Value;
356 var t = new TupleTypeExpr (ta, names, loc);
357 type = t.ResolveAsType (rc) ?? InternalType.ErrorType;
362 public override void Emit (EmitContext ec)
364 foreach (var el in elements) {
368 // TODO: Needs arguments check
369 var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec;
371 ec.Emit (OpCodes.Newobj, ctor);
374 public override void Error_ValueCannotBeConverted (ResolveContext rc, TypeSpec target, bool expl)
376 rc.Report.Error (8135, Location, "Tuple literal `{0}' cannot be converted to type `{1}'", type.GetSignatureForError (), target.GetSignatureForError ());
381 // Used when converting from a tuple literal or tuple instance to different tuple type
383 class TupleLiteralConversion : Expression
385 List<Expression> elements;
388 public TupleLiteralConversion (Expression source, TypeSpec type, List<Expression> elements, Location loc)
390 this.source = source;
392 this.elements = elements;
395 eclass = source.eclass;
398 public override Expression CreateExpressionTree (ResolveContext rc)
400 rc.Report.Error (8144, loc, "An expression tree cannot contain a tuple conversion");
404 protected override Expression DoResolve (ResolveContext rc)
406 // Should not be reached
407 throw new NotSupportedException ();
410 public override void Emit (EmitContext ec)
412 if (!(source is TupleLiteral)) {
413 var assign = source as CompilerAssign;
415 assign.EmitStatement (ec);
420 foreach (var el in elements) {
424 // TODO: Needs arguments check
425 var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec;
427 ec.Emit (OpCodes.Newobj, ctor);
431 class TupleDeconstruct : ExpressionStatement, IAssignMethod
434 List<Expression> targetExprs;
435 List<LocalVariable> variablesToInfer;
438 public TupleDeconstruct (List<Expression> targetExprs, Expression source, Location loc)
440 this.source = source;
441 this.targetExprs = targetExprs;
445 public TupleDeconstruct (List<Expression> targetExprs, List<LocalVariable> variables, Expression source, Location loc)
446 : this (targetExprs, source, loc)
448 this.variablesToInfer = variables;
451 public override Expression CreateExpressionTree (ResolveContext ec)
453 ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
455 throw new NotImplementedException ();
458 protected override Expression DoResolve (ResolveContext rc)
460 var src = source.Resolve (rc);
464 if (InternalType.HasNoType (src.Type)) {
465 rc.Report.Error (8131, source.Location, "Deconstruct assignment requires an expression with a type on the right-hand-side");
469 var src_type = src.Type;
471 if (src_type.IsTupleType) {
472 if (src_type.Arity != targetExprs.Count) {
473 rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{1}' variables",
474 src_type.Arity.ToString (), targetExprs.Count.ToString ());
478 var tupleLiteral = src as TupleLiteral;
479 if (tupleLiteral == null && !ExpressionAnalyzer.IsInexpensiveLoad (src)) {
480 var expr_variable = LocalVariable.CreateCompilerGenerated (source.Type, rc.CurrentBlock, loc);
481 source = new CompilerAssign (expr_variable.CreateReferenceExpression (rc, loc), source, loc);
482 instance = expr_variable.CreateReferenceExpression (rc, loc);
485 for (int i = 0; i < targetExprs.Count; ++i) {
486 var tle = src_type.TypeArguments [i];
488 var lv = variablesToInfer? [i];
490 if (InternalType.HasNoType (tle)) {
491 rc.Report.Error (8130, Location, "Cannot infer the type of implicitly-typed deconstruction variable `{0}'", lv.Name);
492 lv.Type = InternalType.ErrorType;
497 lv.PrepareAssignmentAnalysis ((BlockContext) rc);
501 var element_src = tupleLiteral == null ? new MemberAccess (instance, NamedTupleSpec.GetElementPropertyName (i)) : tupleLiteral.Elements [i].Expr;
502 targetExprs [i] = new SimpleAssign (targetExprs [i], element_src).Resolve (rc);
505 eclass = ExprClass.Value;
510 if (src_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
511 rc.Report.Error (8133, loc, "Cannot deconstruct dynamic objects");
516 var args = new Arguments (targetExprs.Count);
517 foreach (var t in targetExprs) {
518 args.Add (new Argument (t, Argument.AType.Out));
521 var invocation = new Invocation (new MemberAccess (src, "Deconstruct"), args);
522 var res = invocation.Resolve (rc);
525 throw new NotImplementedException ("Custom deconstruct");
528 public override void Emit (EmitContext ec)
530 throw new NotImplementedException ();
533 public override void EmitStatement (EmitContext ec)
535 if (instance != null)
536 ((ExpressionStatement) source).EmitStatement (ec);
538 foreach (ExpressionStatement expr in targetExprs)
539 expr.EmitStatement (ec);
542 public void Emit (EmitContext ec, bool leave_copy)
544 throw new NotImplementedException ();
547 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
550 throw new NotImplementedException ();
552 foreach (var lv in variablesToInfer)
553 lv.CreateBuilder (ec);
558 public override void FlowAnalysis (FlowAnalysisContext fc)
560 foreach (var expr in targetExprs)
561 expr.FlowAnalysis (fc);
564 public void SetGeneratedFieldAssigned (FlowAnalysisContext fc)
566 if (variablesToInfer == null)
569 foreach (var lv in variablesToInfer)
570 fc.SetVariableAssigned (lv.VariableInfo);