Merge pull request #5668 from kumpera/wasm-work-p4
[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_ntuple = type as NamedTupleSpec;
238                         if (btype_ntuple == null && mtype_ntuple == null)
239                                 return true;
240
241                         if (btype_ntuple == null || mtype_ntuple == null)
242                                 return false;
243
244                         var b_elements = btype_ntuple.elements;
245                         var m_elements = mtype_ntuple.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, IAssignMethod
432         {
433                 Expression source;
434                 List<Expression> targetExprs;
435                 List<LocalVariable> variablesToInfer;
436                 Expression instance;
437
438                 public TupleDeconstruct (List<Expression> targetExprs, Expression source, Location loc)
439                 {
440                         this.source = source;
441                         this.targetExprs = targetExprs;
442                         this.loc = loc;
443                 }
444
445                 public TupleDeconstruct (List<Expression> targetExprs, List<LocalVariable> variables, Expression source, Location loc)
446                         : this (targetExprs, source, loc)
447                 {
448                         this.variablesToInfer = variables;
449                 }
450
451                 public override Expression CreateExpressionTree (ResolveContext ec)
452                 {
453                         ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
454                         
455                         throw new NotImplementedException ();
456                 }
457
458                 protected override Expression DoResolve (ResolveContext rc)
459                 {
460                         var src = source.Resolve (rc);
461                         if (src == null)
462                                 return null;
463                         
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");
466                                 return null;
467                         }
468
469                         var src_type = src.Type;
470
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 ());
475                                         return null;
476                                 }
477
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);
483                                 }
484
485                                 for (int i = 0; i < targetExprs.Count; ++i) {
486                                         var tle = src_type.TypeArguments [i];
487
488                                         var lv = variablesToInfer? [i];
489                                         if (lv != null) {
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;
493                                                         continue;
494                                                 }
495
496                                                 lv.Type = tle;
497                                                 lv.PrepareAssignmentAnalysis ((BlockContext) rc);
498                                         }
499
500
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);
503                                 }
504
505                                 eclass = ExprClass.Value;
506                                 type = src.Type;
507                                 return this;
508                         }
509
510                         if (src_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
511                                 rc.Report.Error (8133, loc, "Cannot deconstruct dynamic objects");
512                                 return null;
513                         }
514
515                         /*
516                         var args = new Arguments (targetExprs.Count);
517                         foreach (var t in targetExprs) {
518                                 args.Add (new Argument (t, Argument.AType.Out));
519                         }
520
521                         var invocation = new Invocation (new MemberAccess (src, "Deconstruct"), args);
522                         var res = invocation.Resolve (rc);
523                         */
524
525                         throw new NotImplementedException ("Custom deconstruct");
526                 }
527
528                 public override void Emit (EmitContext ec)
529                 {
530                         throw new NotImplementedException ();
531                 }
532
533                 public override void EmitStatement (EmitContext ec)
534                 {
535                         if (instance != null)
536                                 ((ExpressionStatement) source).EmitStatement (ec);
537                         
538                         foreach (ExpressionStatement expr in targetExprs)
539                                 expr.EmitStatement (ec);
540                 }
541
542                 public void Emit (EmitContext ec, bool leave_copy)
543                 {
544                         throw new NotImplementedException ();
545                 }
546
547                 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
548                 {
549                         if (leave_copy)
550                                 throw new NotImplementedException ();
551
552                         foreach (var lv in variablesToInfer)
553                                 lv.CreateBuilder (ec);
554
555                         EmitStatement (ec);
556                 }
557
558                 public override void FlowAnalysis (FlowAnalysisContext fc)
559                 {
560                         foreach (var expr in targetExprs)
561                                 expr.FlowAnalysis (fc);
562                 }
563
564                 public void SetGeneratedFieldAssigned (FlowAnalysisContext fc)
565                 {
566                         if (variablesToInfer == null)
567                                 return;
568
569                         foreach (var lv in variablesToInfer)
570                                 fc.SetVariableAssigned (lv.VariableInfo);
571                 }
572         }
573 }