diff options
author | Marek Safar <marek.safar@gmail.com> | 2018-01-22 16:59:55 +0300 |
---|---|---|
committer | Marek Safar <marek.safar@gmail.com> | 2018-01-29 19:46:25 +0300 |
commit | f4ac568f87d48a4b69edba7a49f2332d7db64282 (patch) | |
tree | 0dde99e0333728fe2c90fd65ccabfb85d36d72c5 /mcs | |
parent | 62f7ec0ae853de341cc344dcf1bc5f9b2f6f347b (diff) |
[mcs] Implements C# 7 tuples implicit deconstruction
Diffstat (limited to 'mcs')
-rw-r--r-- | mcs/errors/cs8184.cs | 10 | ||||
-rw-r--r-- | mcs/mcs/cs-parser.jay | 53 | ||||
-rw-r--r-- | mcs/mcs/expression.cs | 9 | ||||
-rw-r--r-- | mcs/mcs/tuples.cs | 69 | ||||
-rw-r--r-- | mcs/tests/test-tuple-09.cs | 19 | ||||
-rw-r--r-- | mcs/tests/ver-il-net_4_x.xml | 50 |
6 files changed, 166 insertions, 44 deletions
diff --git a/mcs/errors/cs8184.cs b/mcs/errors/cs8184.cs new file mode 100644 index 00000000000..19a4685d7bd --- /dev/null +++ b/mcs/errors/cs8184.cs @@ -0,0 +1,10 @@ +// CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side +// Line: 8 + +class X +{ + public static void Main () + { + (int a, b) = (1, 2); + } +}
\ No newline at end of file diff --git a/mcs/mcs/cs-parser.jay b/mcs/mcs/cs-parser.jay index e2e85431d99..b27caaa0982 100644 --- a/mcs/mcs/cs-parser.jay +++ b/mcs/mcs/cs-parser.jay @@ -5097,17 +5097,25 @@ assignment_expression $$ = new CompoundAssign (Binary.Operator.ExclusiveOr, (Expression) $1, (Expression) $3); lbag.AddLocation ($$, GetLocation ($2)); } - | OPEN_PARENS_DECONSTRUCT deconstruct_exprs CLOSE_PARENS ASSIGN expression + | OPEN_PARENS_DECONSTRUCT deconstruct_assignment CLOSE_PARENS ASSIGN expression { if (lang_version < LanguageVersion.V_7) FeatureIsNotAvailable (GetLocation ($1), "tuples"); - var exprs = (List<Expression>) $2; + var exprs = (List<Expression>) $2; + $$ = new TupleDeconstruct (exprs, (Expression) $5, GetLocation ($4)); + } + | OPEN_PARENS_DECONSTRUCT deconstruct_declaration CLOSE_PARENS ASSIGN expression + { + if (lang_version < LanguageVersion.V_7) + FeatureIsNotAvailable (GetLocation ($1), "tuples"); + + var exprs = (List<BlockVariable>) $2; $$ = new TupleDeconstruct (exprs, (Expression) $5, GetLocation ($4)); } ; -deconstruct_exprs +deconstruct_assignment : expression COMMA expression { $$ = new List<Expression> () { @@ -5115,7 +5123,7 @@ deconstruct_exprs (Expression) $3 }; } - | deconstruct_exprs COMMA expression + | deconstruct_assignment COMMA expression { var src = (List<Expression>) $1; src.Add ((Expression) $3); @@ -5123,6 +5131,43 @@ deconstruct_exprs } ; +deconstruct_declaration + : variable_type identifier_inside_body + { + var lt = (LocatedToken) $2; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + $$ = new List<BlockVariable> (2) { + new BlockVariable ((FullNamedExpression) $1, li) + }; + } + | deconstruct_declaration COMMA variable_type identifier_inside_body + { + var lt = (LocatedToken) $4; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + current_block.AddLocalName (li); + + var src = (List<BlockVariable>) $1; + src.Add (new BlockVariable ((FullNamedExpression) $3, li)); + $$ = src; + } + | deconstruct_declaration COMMA identifier_inside_body + { + var lt = (LocatedToken) $3; + var li = new LocalVariable (current_block, lt.Value, lt.Location); + + if (lt.Value != "_") { + report.Error (8184, lt.Location, "A deconstruction cannot mix declarations and expressions on the left-hand-side"); + } else { + li.Type = InternalType.Discard; + } + + var src = (List<BlockVariable>) $1; + src.Add (new BlockVariable (new TypeExpression (li.Type, lt.Location), li)); + $$ = src; + } + ; + lambda_parameter_list : lambda_parameter { diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs index 1074924828e..cf27d94416e 100644 --- a/mcs/mcs/expression.cs +++ b/mcs/mcs/expression.cs @@ -7208,8 +7208,7 @@ namespace Mono.CSharp { var sn = expr as SimpleName; if (sn != null && sn.Name == "var" && sn.Arity == 0 && arguments?.Count > 1) { - var targets = new List<Expression> (arguments.Count); - var variables = new List<LocalVariable> (arguments.Count); + var variables = new List<BlockVariable> (arguments.Count); foreach (var arg in arguments) { var arg_sn = arg.Expr as SimpleName; if (arg_sn == null || arg_sn.Arity != 0) { @@ -7219,12 +7218,10 @@ namespace Mono.CSharp var lv = new LocalVariable (rc.CurrentBlock, arg_sn.Name, arg.Expr.Location); rc.CurrentBlock.AddLocalName (lv); - variables.Add (lv); - - targets.Add (new LocalVariableReference (lv, arg_sn.Location)); + variables.Add (new BlockVariable (new VarExpr (lv.Location), lv)); } - var res = new TupleDeconstruct (targets, variables, right_side, loc); + var res = new TupleDeconstruct (variables, right_side, loc); return res.Resolve (rc); } diff --git a/mcs/mcs/tuples.cs b/mcs/mcs/tuples.cs index daeb7e28215..901efdc9541 100644 --- a/mcs/mcs/tuples.cs +++ b/mcs/mcs/tuples.cs @@ -432,7 +432,7 @@ namespace Mono.CSharp { Expression source; List<Expression> targetExprs; - List<LocalVariable> variablesToInfer; + List<BlockVariable> variables; Expression instance; public TupleDeconstruct (List<Expression> targetExprs, Expression source, Location loc) @@ -442,10 +442,11 @@ namespace Mono.CSharp this.loc = loc; } - public TupleDeconstruct (List<Expression> targetExprs, List<LocalVariable> variables, Expression source, Location loc) - : this (targetExprs, source, loc) + public TupleDeconstruct (List<BlockVariable> variables, Expression source, Location loc) { - this.variablesToInfer = variables; + this.source = source; + this.variables = variables; + this.loc = loc; } public override Expression CreateExpressionTree (ResolveContext ec) @@ -469,9 +470,18 @@ namespace Mono.CSharp var src_type = src.Type; if (src_type.IsTupleType) { - if (src_type.Arity != targetExprs.Count) { + int target_count; + + if (targetExprs == null) { + target_count = variables.Count; + targetExprs = new List<Expression> (target_count); + } else { + target_count = targetExprs.Count; + } + + if (src_type.Arity != target_count) { rc.Report.Error (8132, loc, "Cannot deconstruct a tuple of `{0}' elements into `{1}' variables", - src_type.Arity.ToString (), targetExprs.Count.ToString ()); + src_type.Arity.ToString (CultureInfo.InvariantCulture), target_count.ToString (CultureInfo.InvariantCulture)); return null; } @@ -482,21 +492,35 @@ namespace Mono.CSharp instance = expr_variable.CreateReferenceExpression (rc, loc); } - for (int i = 0; i < targetExprs.Count; ++i) { + for (int i = 0; i < target_count; ++i) { var tle = src_type.TypeArguments [i]; - var lv = variablesToInfer? [i]; - if (lv != null) { - if (InternalType.HasNoType (tle)) { - rc.Report.Error (8130, Location, "Cannot infer the type of implicitly-typed deconstruction variable `{0}'", lv.Name); - lv.Type = InternalType.ErrorType; + if (variables != null) { + var variable = variables [i].Variable; + + if (variable.Type == InternalType.Discard) { + variables [i] = null; + targetExprs.Add (EmptyExpressionStatement.Instance); continue; } - lv.Type = tle; - lv.PrepareAssignmentAnalysis ((BlockContext) rc); - } + var variable_type = variables [i].TypeExpression; + + targetExprs.Add (new LocalVariableReference (variable, variable.Location)); + if (variable_type is VarExpr) { + if (InternalType.HasNoType (tle)) { + rc.Report.Error (8130, Location, "Cannot infer the type of implicitly-typed deconstruction variable `{0}'", variable.Name); + tle = InternalType.ErrorType; + } + + variable.Type = tle; + } else { + variable.Type = variable_type.ResolveAsType (rc); + } + + variable.PrepareAssignmentAnalysis ((BlockContext)rc); + } var element_src = tupleLiteral == null ? new MemberAccess (instance, NamedTupleSpec.GetElementPropertyName (i)) : tupleLiteral.Elements [i].Expr; targetExprs [i] = new SimpleAssign (targetExprs [i], element_src).Resolve (rc); @@ -542,6 +566,12 @@ namespace Mono.CSharp public override void EmitStatement (EmitContext ec) { + if (variables != null) { + foreach (var lv in variables) { + lv?.Variable.CreateBuilder (ec); + } + } + if (instance != null) ((ExpressionStatement) source).EmitStatement (ec); @@ -559,9 +589,6 @@ namespace Mono.CSharp if (leave_copy) throw new NotImplementedException (); - foreach (var lv in variablesToInfer) - lv.CreateBuilder (ec); - EmitStatement (ec); } @@ -573,11 +600,11 @@ namespace Mono.CSharp public void SetGeneratedFieldAssigned (FlowAnalysisContext fc) { - if (variablesToInfer == null) + if (variables == null) return; - foreach (var lv in variablesToInfer) - fc.SetVariableAssigned (lv.VariableInfo); + foreach (var lv in variables) + fc.SetVariableAssigned (lv.Variable.VariableInfo); } } }
\ No newline at end of file diff --git a/mcs/tests/test-tuple-09.cs b/mcs/tests/test-tuple-09.cs new file mode 100644 index 00000000000..3f15cae6cf1 --- /dev/null +++ b/mcs/tests/test-tuple-09.cs @@ -0,0 +1,19 @@ +using System; + +class TupleDeconstructionDeclaration +{ + public static int Main () + { + (string s, long l) = GetValues (); + (var vs, var vl) = GetValues (); + (object o, var vl2) = GetValues (); + (string ds, _) = GetValues (); + + return 0; + } + + static (string, long) GetValues () + { + return ("a", 3); + } +}
\ No newline at end of file diff --git a/mcs/tests/ver-il-net_4_x.xml b/mcs/tests/ver-il-net_4_x.xml index e7e10c749da..6e082d7c592 100644 --- a/mcs/tests/ver-il-net_4_x.xml +++ b/mcs/tests/ver-il-net_4_x.xml @@ -68568,19 +68568,6 @@ </method> </type> </test> - <test name="test-default-03.cs"> - <type name="X"> - <method name="Int32 Main()" attrs="150"> - <size>25</size> - </method> - <method name="Byte[] Test(S`1[System.Byte])" attrs="145"> - <size>10</size> - </method> - <method name="Void .ctor()" attrs="6278"> - <size>7</size> - </method> - </type> - </test> <test name="test-dictinit-01.cs"> <type name="Program"> <method name="Int32 Main()" attrs="145"> @@ -73552,6 +73539,43 @@ </method> </type> </test> + <test name="test-tuple-08.cs"> + <type name="X"> + <method name="Void Main()" attrs="150"> + <size>19</size> + </method> + <method name="System.Threading.Tasks.Task Test()" attrs="129"> + <size>41</size> + </method> + <method name="System.Threading.Tasks.Task`1[System.ValueTuple`2[System.Int32,System.Int32]] Waiting()" attrs="129"> + <size>21</size> + </method> + <method name="Void .ctor()" attrs="6278"> + <size>7</size> + </method> + </type> + <type name="X+<Test>c__async0"> + <method name="Void MoveNext()" attrs="486"> + <size>212</size> + </method> + <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486"> + <size>13</size> + </method> + </type> + </test> + <test name="test-tuple-09.cs"> + <type name="TupleDeconstructionDeclaration"> + <method name="Int32 Main()" attrs="150"> + <size>99</size> + </method> + <method name="System.ValueTuple`2[System.String,System.Int64] GetValues()" attrs="145"> + <size>21</size> + </method> + <method name="Void .ctor()" attrs="6278"> + <size>7</size> + </method> + </type> + </test> <test name="test-var-01.cs"> <type name="Test"> <method name="Int32 Main()" attrs="150"> |