e4f56ca131b6a8443f087312a8316477c3540101
[mono.git] / mcs / mcs / tuples.cs
1 //
2 // tuples.cs: Tuples types
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright (C) Microsoft Corporation.
10 //
11
12 using System;
13 using System.Collections.Generic;
14 using System.Text;
15 using System.Globalization;
16
17 #if STATIC
18 using MetaType = IKVM.Reflection.Type;
19 using IKVM.Reflection;
20 using IKVM.Reflection.Emit;
21 #else
22 using MetaType = System.Type;
23 using System.Reflection;
24 using System.Reflection.Emit;
25 #endif
26
27 namespace Mono.CSharp
28 {
29         class TupleTypeExpr : TypeExpr
30         {
31                 TypeArguments elements;
32                 List<string> names;
33
34                 public TupleTypeExpr (TypeArguments elements, List<string> names, Location loc)
35                 {
36                         this.elements = elements;
37                         this.names = names;
38                         this.loc = loc;
39                 }
40
41                 public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
42                 {
43                         var length = elements.Count;
44                         if (length > 7)
45                                 throw new NotImplementedException ("tuples > 7");
46
47                         eclass = ExprClass.Type;
48
49                         var otype = mc.Module.PredefinedTypes.Tuples [length - 1].Resolve ();
50                         if (otype == null)
51                                 return null;
52
53                         GenericTypeExpr ctype = new GenericTypeExpr (otype, elements, loc);
54                         type = ctype.ResolveAsType (mc);
55
56                         if (names != null && CheckElementNames (mc) && type != null) {
57                                 type = NamedTupleSpec.MakeType (mc.Module, (InflatedTypeSpec) type, names);
58                         }
59
60                         return type;
61                 }
62
63                 bool CheckElementNames (IMemberContext mc)
64                 {
65                         int first_name = -1;
66                         for (int i = 0; i < names.Count; ++i) {
67                                 var name = names [i];
68                                 if (name == null)
69                                         continue;
70
71                                 if (IsReservedName (name)) {
72                                         mc.Module.Compiler.Report.Error (8126, loc, "The tuple element name `{0}' is reserved", name);
73                                         names [i] = null;
74                                         continue;
75                                 }
76
77                                 if (name.StartsWith ("Item", StringComparison.Ordinal)) {
78                                         var idx = name.Substring (4);
79                                         uint value;
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);
82                                                 names [i] = null;
83                                                 continue;
84                                         }
85                                 }
86
87                                 if (first_name < 0) {
88                                         first_name = i;
89                                         continue;
90                                 }
91
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);
95                                                 names [i] = null;
96                                                 break;
97                                         }
98                                 }
99                         }
100
101                         return first_name >= 0;
102                 }
103
104                 static bool IsReservedName (string name)
105                 {
106                         switch (name) {
107                         case "CompareTo":
108                         case "Deconstruct":
109                         case "Equals":
110                         case "GetHashCode":
111                         case "Rest":
112                         case "ToString":
113                                 return true;
114                         default:
115                                 return false;
116                         }
117                 }
118
119                 public override string GetSignatureForError ()
120                 {
121                         var sb = new StringBuilder ();
122                         for (int i = 0; i < elements.Count; ++i) {
123                                 sb.Append (elements.Arguments [i].GetSignatureForError ());
124
125                                 if (names [i] != null) {
126                                         sb.Append (" ");
127                                         sb.Append (names [i]);
128                                 }
129
130                                 if (i != 0)
131                                         sb.Append (",");
132                         }
133
134                         return sb.ToString ();
135                 }
136         }
137
138         public class NamedTupleSpec : TypeSpec
139         {
140                 InflatedTypeSpec tuple;
141                 IList<string> elements;
142
143                 private NamedTupleSpec (InflatedTypeSpec tupleDefinition, IList<string> elements)
144                         : base (tupleDefinition.Kind, tupleDefinition.DeclaringType, tupleDefinition.MemberDefinition, null, tupleDefinition.Modifiers)
145                 {
146                         tuple = tupleDefinition;
147                         this.elements = elements;
148
149                         state |= StateFlags.HasNamedTupleElement | StateFlags.Tuple;
150                 }
151
152                 public IList<string> Elements {
153                         get {
154                                 return elements;
155                         }
156                 }
157
158                 public override TypeSpec [] TypeArguments {
159                         get {
160                                 return tuple.TypeArguments;
161                         }
162                 }
163
164                 protected override void InitializeMemberCache (bool onlyTypes)
165                 {
166                         cache = tuple.MemberCache;
167                 }
168
169                 public override MetaType GetMetaInfo ()
170                 {
171                         return tuple.GetMetaInfo ();
172                 }
173
174                 public MemberSpec FindElement (IMemberContext mc, string name, Location loc)
175                 {
176                         // TODO: cache it
177                         for (int i = 0; i < elements.Count; ++i) {
178                                 var ename = elements [i];
179                                 if (ename == null || ename != name)
180                                         continue;
181
182                                 var member_name = GetElementPropertyName (i);
183                                 var ms = MemberCache.FindMember (tuple, MemberFilter.Field (member_name, null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly);
184                                 if (ms == null) {
185                                         mc.Module.Compiler.Report.Error (8128, loc, "Member `{0}' was not found on type '{1}'", member_name, tuple.GetSignatureForError ());
186                                         return null;
187                                 }
188
189                                 return ms;
190                         }
191
192                         return null;
193                 }
194
195                 public override string GetSignatureForError ()
196                 {
197                         //
198                         // csc reports names as well but it seems to me redundant when
199                         // they are not included in any type conversion
200                         //
201                         return tuple.GetSignatureForError ();
202                 }
203
204                 public string GetSignatureForErrorWithNames ()
205                 {
206                         // TODO: Include names
207                         return tuple.GetSignatureForError ();
208                 }
209
210                 public static NamedTupleSpec MakeType (ModuleContainer module, InflatedTypeSpec tupleType, IList<string> names)
211                 {
212                         // TODO: cache it
213                         return new NamedTupleSpec (tupleType, names);
214                 }
215
216                 public static string GetElementPropertyName (int index)
217                 {
218                         return "Item" + (index + 1).ToString (CultureInfo.InvariantCulture);
219                 }
220
221                 public static bool CheckOverrideName (IParametersMember member, IParametersMember baseMember)
222                 {
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])) {
227                                         return false;
228                                 }
229                         }
230
231                         return true;
232                 }
233         
234                 public static bool CheckOverrideName (TypeSpec type, TypeSpec baseType)
235                 {
236                         var btype_ntuple = baseType as NamedTupleSpec;
237                         var mtype_ntupe = type as NamedTupleSpec;
238                         if (btype_ntuple == null && mtype_ntupe == null)
239                                 return true;
240
241                         if (btype_ntuple != null || mtype_ntupe != null)
242                                 return false;
243
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])
248                                         return false;
249                         }
250
251                         return true;
252                 }
253         }
254
255         class TupleLiteralElement
256         {
257                 public TupleLiteralElement (string name, Expression expr, Location loc)
258                 {
259                         this.Name = name;
260                         this.Expr = expr;
261                         this.Location = loc;
262                 }
263
264                 public TupleLiteralElement (Expression expr)
265                 {
266                         this.Expr = expr;
267                         this.Location = expr.Location;
268                 }
269
270                 public string Name { get; private set; }
271                 public Expression Expr { get; set; }
272                 public Location Location { get; private set; }
273         }
274
275         sealed class TupleLiteral : Expression
276         {
277                 List<TupleLiteralElement> elements;
278
279                 public TupleLiteral (List<TupleLiteralElement> elements, Location loc)
280                 {
281                         this.elements = elements;
282                         this.loc = loc;
283                 }
284
285                 public List<TupleLiteralElement> Elements {
286                         get {
287                                 return elements;
288                         }
289                 }
290
291                 public static bool ContainsNoTypeElement (TypeSpec type)
292                 {
293                         var ta = type.TypeArguments;
294
295                         for (int i = 0; i < ta.Length; ++i) {
296                                 var et = ta [i];
297                                 if (InternalType.HasNoType (et))
298                                         return true;
299
300                                 if (et.IsTupleType && ContainsNoTypeElement (et))
301                                         return true;
302                         }
303
304                         return false;
305                 }
306
307                 public override Expression CreateExpressionTree (ResolveContext rc)
308                 {
309                         rc.Report.Error (8143, loc, "An expression tree cannot contain a tuple literal");
310                         return ErrorExpression.Instance;
311                 }
312
313                 protected override Expression DoResolve (ResolveContext rc)
314                 {
315                         var ta = new TypeArguments ();
316                         List<string> names = null;
317
318                         for (int i = 0; i < elements.Count; ++i) {
319                                 var el = elements [i];
320                                 var expr = el.Expr.Resolve (rc);
321                                 if (expr == null) {
322                                         el.Expr = null;
323                                         ta = null;
324                                         continue;
325                                 }
326
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 ());
329                                         expr = null;
330                                         ta = null;
331                                         continue;
332                                 }
333
334                                 if (el.Name != null) {
335                                         if (names == null) {
336                                                 names = new List<string> ();
337                                                 for (int ii = 0; ii < i; ++ii) {
338                                                         names.Add (null);
339                                                 }
340                                         }
341
342                                         names.Add (el.Name);
343                                 }
344
345                                 el.Expr = expr;
346
347                                 if (ta != null)
348                                         ta.Add (new TypeExpression (expr.Type, expr.Location));
349                         }
350
351                         eclass = ExprClass.Value;
352
353                         if (ta == null)
354                                 return null;
355
356                         var t = new TupleTypeExpr (ta, names, loc);
357                         type = t.ResolveAsType (rc) ?? InternalType.ErrorType;
358
359                         return this;
360                 }
361
362                 public override void Emit (EmitContext ec)
363                 {
364                         foreach (var el in elements) {
365                                 el.Expr.Emit (ec);
366                         }
367
368                         // TODO: Needs arguments check
369                         var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec;
370
371                         ec.Emit (OpCodes.Newobj, ctor);
372                 }
373
374                 public override void Error_ValueCannotBeConverted (ResolveContext rc, TypeSpec target, bool expl)
375                 {
376                         rc.Report.Error (8135, Location, "Tuple literal `{0}' cannot be converted to type `{1}'", type.GetSignatureForError (), target.GetSignatureForError ());
377                 }
378         }
379
380         //
381         // Used when converting from a tuple literal or tuple instance to different tuple type
382         //
383         class TupleLiteralConversion : Expression
384         {
385                 List<Expression> elements;
386                 Expression source;
387
388                 public TupleLiteralConversion (Expression source, TypeSpec type, List<Expression> elements, Location loc)
389                 {
390                         this.source = source;
391                         this.type = type;
392                         this.elements = elements;
393                         this.loc = loc;
394
395                         eclass = source.eclass;
396                 }
397
398                 public override Expression CreateExpressionTree (ResolveContext rc)
399                 {
400                         rc.Report.Error (8144, loc, "An expression tree cannot contain a tuple conversion");
401                         return null;
402                 }
403
404                 protected override Expression DoResolve (ResolveContext rc)
405                 {
406                         // Should not be reached
407                         throw new NotSupportedException ();
408                 }
409
410                 public override void Emit (EmitContext ec)
411                 {
412                         if (!(source is TupleLiteral)) {
413                                 var assign = source as CompilerAssign;
414                                 if (assign != null)
415                                         assign.EmitStatement (ec);
416                                 else
417                                         source.Emit (ec);
418                         }
419                         
420                         foreach (var el in elements) {
421                                 el.Emit (ec);
422                         }
423
424                         // TODO: Needs arguments check
425                         var ctor = MemberCache.FindMember (type, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly) as MethodSpec;
426
427                         ec.Emit (OpCodes.Newobj, ctor);
428                 }
429         }
430
431         class TupleDeconstruct : ExpressionStatement
432         {
433                 Expression source;
434                 List<Expression> targetExprs;
435
436                 public TupleDeconstruct (List<Expression> targetExprs, Expression source, Location loc)
437                 {
438                         this.source = source;
439                         this.targetExprs = targetExprs;
440                         this.loc = loc;
441                 }
442
443                 public override Expression CreateExpressionTree (ResolveContext ec)
444                 {
445                         ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
446                         
447                         throw new NotImplementedException ();
448                 }
449
450                 protected override Expression DoResolve (ResolveContext rc)
451                 {
452                         var src = source.Resolve (rc);
453                         if (src == null)
454                                 return null;
455                         
456                         if (InternalType.HasNoType (src.Type)) {
457                                 rc.Report.Error (8131, source.Location, "Deconstruct assignment requires an expression with a type on the right-hand-side");
458                                 return null;
459                         }
460
461                         var tupleLiteral = src as TupleLiteral;
462                         if (tupleLiteral != null) {
463                                 if (tupleLiteral.Elements.Count != targetExprs.Count) {
464                                         rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{0}' variables",
465                                                 tupleLiteral.Elements.Count.ToString (), targetExprs.Count.ToString ());
466                                         return null;
467                                 }
468
469                                 for (int i = 0; i < targetExprs.Count; ++i) {
470                                         targetExprs [i] = new SimpleAssign (targetExprs [i], tupleLiteral.Elements [i].Expr).Resolve (rc);
471                                 }
472
473                                 eclass = ExprClass.Value;
474                                 type = src.Type;
475                                 return this;
476                         }
477
478                         if (src.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
479                                 rc.Report.Error (8133, loc, "Cannot deconstruct dynamic objects");
480                                 return null;
481                         }
482
483                         throw new NotImplementedException ("Deconstruct assignment");
484                 }
485
486                 public override void Emit (EmitContext ec)
487                 {
488                         throw new NotImplementedException ();
489                 }
490
491                 public override void EmitStatement (EmitContext ec)
492                 {
493                         foreach (ExpressionStatement expr in targetExprs)
494                                 expr.EmitStatement (ec);
495                 }
496
497                 public override void FlowAnalysis (FlowAnalysisContext fc)
498                 {
499                         foreach (var expr in targetExprs)
500                                 expr.FlowAnalysis (fc);
501                 }
502         }
503 }