提交 ce93d1c7 编写于 作者: J Julien Couvreur 提交者: GitHub

Avoid creating "steps" in deconstruction binding and optimize output (#17027)

上级 23271742
......@@ -171,13 +171,13 @@ internal override BoundStatement BindForEachDeconstruction(DiagnosticBag diagnos
DeclarationExpressionSyntax declaration = null;
ExpressionSyntax expression = null;
BoundDeconstructionAssignmentOperator deconstruction = BindDeconstruction(
variables,
variables,
right: null,
diagnostics: diagnostics,
rightPlaceholder: valuePlaceholder,
declaration: ref declaration,
expression: ref expression);
variables,
variables,
right: _syntax.Expression,
diagnostics: diagnostics,
rightPlaceholder: valuePlaceholder,
declaration: ref declaration,
expression: ref expression);
return new BoundExpressionStatement(_syntax, deconstruction);
}
......@@ -246,7 +246,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
BoundDeconstructionAssignmentOperator deconstruction = BindDeconstruction(
variables,
variables,
right: null,
right: _syntax.Expression,
diagnostics: diagnostics,
rightPlaceholder: valuePlaceholder,
declaration: ref declaration,
......
......@@ -67,6 +67,18 @@ internal bool IsArrayIndex
}
}
private class DeconstructionUncommonData : UncommonData
{
internal DeconstructionUncommonData(DeconstructionInfo deconstructionInfoOpt, ImmutableArray<Conversion> nestedConversions)
: base(false, false, default(UserDefinedConversionResult), null, nestedConversions)
{
Debug.Assert(!nestedConversions.IsDefaultOrEmpty);
DeconstructionInfo = deconstructionInfoOpt;
}
readonly internal DeconstructionInfo DeconstructionInfo;
}
private Conversion(
ConversionKind kind,
UncommonData uncommonData)
......@@ -117,6 +129,14 @@ internal Conversion(ConversionKind kind, ImmutableArray<Conversion> nestedConver
nestedConversions: nestedConversions);
}
internal Conversion(ConversionKind kind, DeconstructionInfo deconstructionInfo, ImmutableArray<Conversion> nestedConversions)
{
Debug.Assert(kind == ConversionKind.Deconstruction);
this._kind = kind;
_uncommonData = new DeconstructionUncommonData(deconstructionInfo, nestedConversions);
}
internal Conversion SetConversionMethod(MethodSymbol conversionMethod)
{
// we use this method to patch up the conversion method only in two cases -
......@@ -213,6 +233,7 @@ internal static Conversion GetTrivialConversion(ConversionKind kind)
internal static Conversion ImplicitDynamic => new Conversion(ConversionKind.ImplicitDynamic);
internal static Conversion ExplicitDynamic => new Conversion(ConversionKind.ExplicitDynamic);
internal static Conversion InterpolatedString => new Conversion(ConversionKind.InterpolatedString);
internal static Conversion Deconstruction => new Conversion(ConversionKind.Deconstruction);
// trivial conversions that could be underlying in nullable conversion
// NOTE: tuple conversions can be underlying as well, but they are not trivial
......@@ -325,6 +346,15 @@ internal MethodSymbol Method
}
}
internal DeconstructionInfo DeconstructionInfo
{
get
{
var uncommonData = (DeconstructionUncommonData)_uncommonData;
return uncommonData == null ? default(DeconstructionInfo) : uncommonData.DeconstructionInfo;
}
}
// CONSIDER: public?
internal bool IsValid
{
......@@ -869,5 +899,52 @@ public override int GetHashCode()
{
return !(left == right);
}
#if DEBUG
internal string Dump()
{
return TreeDumper.DumpCompact(Dump(this));
TreeDumperNode Dump(Conversion self)
{
var sub = new System.Collections.Generic.List<TreeDumperNode>();
if ((object)self.Method != null)
{
sub.Add(new TreeDumperNode("method", self.Method.ToDisplayString(), null));
}
if ((object)self.DeconstructionInfo != null)
{
sub.Add(new TreeDumperNode("deconstructionInfo", null,
new[] { BoundTreeDumperNodeProducer.MakeTree(self.DeconstructionInfo.Invocation)}));
}
var underlyingConversions = self.UnderlyingConversions;
if (!underlyingConversions.IsDefaultOrEmpty)
{
sub.Add(new TreeDumperNode($"underlyingConversions[{underlyingConversions.Length}]", null,
underlyingConversions.SelectAsArray(c => Dump(c))));
}
return new TreeDumperNode("conversion", self.Kind, sub);
}
}
#endif
}
/// <summary>Stores all the information from binding for calling a Deconstruct method.</summary>
internal struct DeconstructionInfo
{
internal DeconstructionInfo(BoundExpression invocation, BoundDeconstructValuePlaceholder inputPlaceholder,
ImmutableArray<BoundDeconstructValuePlaceholder> outputPlaceholders)
{
(Invocation, InputPlaceholder, OutputPlaceholders) = (invocation, inputPlaceholder, outputPlaceholders);
}
readonly internal BoundExpression Invocation;
readonly internal BoundDeconstructValuePlaceholder InputPlaceholder;
readonly internal ImmutableArray<BoundDeconstructValuePlaceholder> OutputPlaceholders;
internal bool IsDefault => (object)Invocation == null;
}
}
......@@ -42,5 +42,6 @@ internal enum ConversionKind : byte
// implement them for compatibility with the native compiler.
IntPtr,
InterpolatedString, // a conversion from an interpolated string to IFormattable or FormattableString
Deconstruction, // The Deconstruction conversion is not part of the language, it is an implementation detail
}
}
......@@ -41,6 +41,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind)
case ConversionKind.PointerToVoid:
case ConversionKind.NullToPointer:
case ConversionKind.InterpolatedString:
case ConversionKind.Deconstruction:
return true;
case ConversionKind.ExplicitNumeric:
......
......@@ -164,7 +164,7 @@ internal virtual string Dump()
}
#endif
private string GetDebuggerDisplay()
internal string GetDebuggerDisplay()
{
var result = GetType().Name;
if (Syntax != null)
......
......@@ -392,29 +392,8 @@
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<Field Name="Left" Type="BoundTupleExpression"/>
<Field Name="Right" Type="BoundExpression"/>
<Field Name="DeconstructSteps" Type="ImmutableArray&lt;BoundDeconstructionDeconstructStep&gt;" Null="disallow" SkipInVisitor="true" />
<Field Name="ConversionSteps" Type="ImmutableArray&lt;BoundDeconstructionAssignmentStep&gt;" Null="disallow" SkipInVisitor="true"/>
<Field Name="AssignmentSteps" Type="ImmutableArray&lt;BoundDeconstructionAssignmentStep&gt;" Null="disallow" SkipInVisitor="true"/>
<Field Name="ConstructionStepsOpt" Type="ImmutableArray&lt;BoundDeconstructionConstructionStep&gt;" Null="allow" SkipInVisitor="true"/>
</Node>
<Node Name="BoundDeconstructionDeconstructStep" Base="BoundNode">
<Field Name="DeconstructInvocationOpt" Type="BoundExpression" Null="allow"/>
<Field Name="InputPlaceholder" Type="BoundDeconstructValuePlaceholder" Null="disallow"/>
<Field Name="OutputPlaceholders" Type="ImmutableArray&lt;BoundDeconstructValuePlaceholder&gt;" Null="disallow"/>
</Node>
<Node Name="BoundDeconstructionAssignmentStep" Base="BoundNode">
<Field Name="Assignment" Type="BoundAssignmentOperator" Null="disallow"/>
<Field Name="OutputPlaceholder" Type="BoundDeconstructValuePlaceholder" Null="disallow"/>
</Node>
<Node Name="BoundDeconstructionConstructionStep" Base="BoundNode">
<Field Name="Construct" Type="BoundExpression" Null="disallow"/>
<Field Name="OutputPlaceholder" Type="BoundDeconstructValuePlaceholder" Null="disallow"/>
<Field Name="Left" Type="BoundTupleExpression" Null="disallow"/>
<Field Name="Right" Type="BoundConversion" Null="disallow"/>
</Node>
<Node Name="BoundNullCoalescingOperator" Base="BoundExpression">
......
......@@ -1024,15 +1024,18 @@ public override void Accept(OperationVisitor visitor)
internal sealed partial class BoundDeconstructionAssignmentOperator : BoundExpression
{
// TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699)
protected override OperationKind ExpressionKind => OperationKind.None;
public override void Accept(OperationVisitor visitor)
{
// TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699)
visitor.VisitNoneOperation(this);
}
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument)
{
// TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699)
return visitor.VisitNoneOperation(this, argument);
}
}
......
......@@ -262,7 +262,7 @@ public override void Accept(OperationVisitor visitor)
internal partial class BoundForEachStatement : IForEachLoopStatement
{
ILocalSymbol IForEachLoopStatement.IterationVariable => this.DeconstructionOpt == null?
ILocalSymbol IForEachLoopStatement.IterationVariable => this.IterationVariables.Length == 1?
this.IterationVariables.FirstOrDefault():
null;
......
......@@ -1233,6 +1233,10 @@ protected virtual void AssignImpl(BoundNode node, BoundExpression value, RefKind
break;
}
case BoundKind.TupleLiteral:
((BoundTupleExpression)node).VisitAllElements((x, self) => self.Assign(x, value: null, refKind: refKind), this);
break;
default:
// Other kinds of left-hand-sides either represent things not tracked (e.g. array elements)
// or errors that have been reported earlier (e.g. assignment to a unary increment)
......@@ -1857,8 +1861,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node)
{
base.VisitDeconstructionAssignmentOperator(node);
node.Left.VisitAllElements((x, self) => self.Assign(x, value: null, refKind: RefKind.None), this);
Assign(node.Left, node.Right);
return null;
}
......
......@@ -546,6 +546,10 @@ protected void VisitLvalue(BoundExpression node)
break;
}
case BoundKind.TupleLiteral:
((BoundTupleExpression)node).VisitAllElements((x, self) => self.VisitLvalue(x), this);
break;
default:
VisitRvalue(node);
break;
......@@ -1675,9 +1679,8 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node)
{
node.Left.VisitAllElements((x, self) => self.VisitLvalue(x), this);
VisitLvalue(node.Left);
VisitRvalue(node.Right);
return null;
}
......
......@@ -136,7 +136,7 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node)
else
{
// deconstruction foreach declares multiple variables
deconstructionAssignment.Left.VisitAllElements((x, self) => self.Visit(x), this);
((BoundTupleExpression)deconstructionAssignment.Left).VisitAllElements((x, self) => self.Visit(x), this);
}
}
}
......
......@@ -268,17 +268,6 @@ private void RemovePlaceholderReplacement(BoundValuePlaceholderBase placeholder)
Debug.Assert(removed);
}
/// <summary>
/// Remove all the listed placeholders.
/// </summary>
private void RemovePlaceholderReplacements(ArrayBuilder<BoundValuePlaceholderBase> placeholders)
{
foreach (var placeholder in placeholders)
{
RemovePlaceholderReplacement(placeholder);
}
}
public override sealed BoundNode VisitOutDeconstructVarPendingInference(OutDeconstructVarPendingInference node)
{
// OutDeconstructVarPendingInference nodes are only used within initial binding, but don't survive past that stage
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
......@@ -17,7 +18,8 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used)
{
var loweredRight = VisitExpression(node.Right);
var right = node.Right;
var loweredRight = VisitExpression(right);
BoundExpression left = node.Left;
BoundExpression loweredLeft;
......@@ -37,7 +39,7 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
if (eventAccess.EventSymbol.IsWindowsRuntimeEvent)
{
Debug.Assert(node.RefKind == RefKind.None);
return VisitWindowsRuntimeEventFieldAssignmentOperator(node.Syntax, eventAccess, node.Right);
return VisitWindowsRuntimeEventFieldAssignmentOperator(node.Syntax, eventAccess, right);
}
goto default;
}
......
......@@ -19655,19 +19655,19 @@ public void PrintCoordinates()
var comp = CreateCompilationWithMscorlib(source, assemblyName: "comp");
comp.VerifyEmitDiagnostics(
// (25,9): error CS8128: Member 'Item1' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// (25,28): error CS8128: Member 'Item1' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// (int x1, int y1) = GetCoordinates();
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "(int x1, int y1) = GetCoordinates()").WithArguments("Item1", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(25, 9),
// (25,9): error CS8128: Member 'Item2' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "GetCoordinates()").WithArguments("Item1", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(25, 28),
// (25,28): error CS8128: Member 'Item2' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// (int x1, int y1) = GetCoordinates();
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "(int x1, int y1) = GetCoordinates()").WithArguments("Item2", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(25, 9),
// (26,9): error CS8128: Member 'Item1' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "GetCoordinates()").WithArguments("Item2", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(25, 28),
// (26,28): error CS8128: Member 'Item1' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// (int x2, int y2) = GetCoordinates2();
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "(int x2, int y2) = GetCoordinates2()").WithArguments("Item1", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(26, 9),
// (26,9): error CS8128: Member 'Item2' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "GetCoordinates2()").WithArguments("Item1", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(26, 28),
// (26,28): error CS8128: Member 'Item2' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// (int x2, int y2) = GetCoordinates2();
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "(int x2, int y2) = GetCoordinates2()").WithArguments("Item2", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(26, 9)
);
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "GetCoordinates2()").WithArguments("Item2", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(26, 28)
);
}
[Fact, WorkItem(13494, "https://github.com/dotnet/roslyn/issues/13494")]
......
......@@ -238,7 +238,6 @@ static void F(System.Collections.Generic.IEnumerable<(int a, int b)> ie)
<slot kind=""0"" offset=""59"" />
<slot kind=""0"" offset=""62"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
......@@ -247,16 +246,16 @@ static void F(System.Collections.Generic.IEnumerable<(int a, int b)> ie)
<entry offset=""0x2"" startLine=""8"" startColumn=""13"" endLine=""8"" endColumn=""15"" />
<entry offset=""0x9"" hidden=""true"" />
<entry offset=""0xb"" startLine=""6"" startColumn=""13"" endLine=""6"" endColumn=""23"" />
<entry offset=""0x24"" startLine=""9"" startColumn=""9"" endLine=""9"" endColumn=""10"" />
<entry offset=""0x25"" startLine=""10"" startColumn=""9"" endLine=""10"" endColumn=""10"" />
<entry offset=""0x26"" startLine=""7"" startColumn=""13"" endLine=""7"" endColumn=""15"" />
<entry offset=""0x30"" hidden=""true"" />
<entry offset=""0x3b"" startLine=""11"" startColumn=""5"" endLine=""11"" endColumn=""6"" />
<entry offset=""0x20"" startLine=""9"" startColumn=""9"" endLine=""9"" endColumn=""10"" />
<entry offset=""0x21"" startLine=""10"" startColumn=""9"" endLine=""10"" endColumn=""10"" />
<entry offset=""0x22"" startLine=""7"" startColumn=""13"" endLine=""7"" endColumn=""15"" />
<entry offset=""0x2c"" hidden=""true"" />
<entry offset=""0x37"" startLine=""11"" startColumn=""5"" endLine=""11"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x3c"">
<scope startOffset=""0xb"" endOffset=""0x26"">
<local name=""a"" il_index=""1"" il_start=""0xb"" il_end=""0x26"" attributes=""0"" />
<local name=""b"" il_index=""2"" il_start=""0xb"" il_end=""0x26"" attributes=""0"" />
<scope startOffset=""0x0"" endOffset=""0x38"">
<scope startOffset=""0xb"" endOffset=""0x22"">
<local name=""a"" il_index=""1"" il_start=""0xb"" il_end=""0x22"" attributes=""0"" />
<local name=""b"" il_index=""2"" il_start=""0xb"" il_end=""0x22"" attributes=""0"" />
</scope>
</scope>
</method>
......
......@@ -6,6 +6,7 @@
using Roslyn.Test.Utilities;
using Xunit;
using System.Linq;
using Microsoft.CodeAnalysis.Semantics;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
......@@ -55,5 +56,48 @@ static void Test2(int y, params int[] x)
IArgument (Matching Parameter: x) (OperationKind.Argument)
ILiteralExpression (Text: null) (OperationKind.LiteralExpression, Type: null, Constant: null)");
}
[Fact]
public void DeconstructionAssignmentFromTuple()
{
var text = @"
public class C
{
public static void M()
{
int x, y, z;
(x, y, z) = (1, 2, 3);
(x, y, z) = new C();
var (a, b) = (1, 2);
}
public void Deconstruct(out int a, out int b, out int c)
{
a = b = c = 1;
}
}";
var compilation = CreateCompilationWithMscorlib(text, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.RegularWithIOperationFeature);
compilation.VerifyDiagnostics();
var tree = compilation.SyntaxTrees.Single();
var model = compilation.GetSemanticModel(tree);
var assignments = tree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>().ToArray();
Assert.Equal("(x, y, z) = (1, 2, 3)", assignments[0].ToString());
IOperation operation1 = model.GetOperation(assignments[0]);
Assert.NotNull(operation1);
Assert.Equal(OperationKind.None, operation1.Kind);
Assert.False(operation1 is IAssignmentExpression);
Assert.Equal("(x, y, z) = new C()", assignments[1].ToString());
IOperation operation2 = model.GetOperation(assignments[1]);
Assert.NotNull(operation2);
Assert.Equal(OperationKind.None, operation2.Kind);
Assert.False(operation2 is IAssignmentExpression);
Assert.Equal("var (a, b) = (1, 2)", assignments[2].ToString());
IOperation operation3 = model.GetOperation(assignments[2]);
Assert.NotNull(operation3);
Assert.Equal(OperationKind.None, operation3.Kind);
Assert.False(operation3 is IAssignmentExpression);
}
}
}
\ No newline at end of file
......@@ -280,7 +280,7 @@ static void Main()
var comp = CreateCompilationWithMscorlib(source);
comp.VerifyDiagnostics(
// (9,18): error CS8129: No Deconstruct instance or extension method was found for type 'C', with 2 out parameters.
// (9,18): error CS8129: No Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type.
// (x, y) = new C();
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(9, 18)
);
......@@ -407,7 +407,7 @@ static void Main()
var comp = CreateCompilationWithMscorlib(source);
comp.VerifyDiagnostics(
// (11,18): error CS8129: No Deconstruct instance or extension method was found for type 'C', with 2 out parameters.
// (11,18): error CS8129: No Deconstruct instance or extension method was found for type 'C', with 2 out parameters and a void return type.
// (x, y) = new C() { Deconstruct = DeconstructMethod };
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C() { Deconstruct = DeconstructMethod }").WithArguments("C", "2").WithLocation(11, 18)
);
......@@ -508,12 +508,12 @@ public void Deconstruct(out int a, out int b)
";
var comp = CreateCompilationWithMscorlib(source, references: s_valueTupleRefs);
comp.VerifyDiagnostics(
// (8,9): error CS0266: Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)
// (8,10): error CS0266: Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)
// (x, y) = new C();
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "(x, y) = new C()").WithArguments("int", "byte").WithLocation(8, 9),
// (8,9): error CS0029: Cannot implicitly convert type 'int' to 'string'
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("int", "byte").WithLocation(8, 10),
// (8,13): error CS0029: Cannot implicitly convert type 'int' to 'string'
// (x, y) = new C();
Diagnostic(ErrorCode.ERR_NoImplicitConv, "(x, y) = new C()").WithArguments("int", "string").WithLocation(8, 9)
Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("int", "string").WithLocation(8, 13)
);
}
......@@ -811,17 +811,15 @@ static void Main()
int y;
(x, y) = (1, 2);
System.Console.WriteLine($""{x} {y}"");
}
}
";
var comp = CreateCompilationWithMscorlib(source, assemblyName: "comp");
var comp = CreateCompilationWithMscorlib(source, assemblyName: "comp", options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
comp.VerifyEmitDiagnostics(
// (22,9): error CS8128: Member 'Item2' was not found on type 'ValueTuple<T1, T2>' from assembly 'comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
// (x, y) = (1, 2);
Diagnostic(ErrorCode.ERR_PredefinedTypeMemberNotFoundInAssembly, "(x, y) = (1, 2)").WithArguments("Item2", "System.ValueTuple<T1, T2>", "comp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(22, 9)
);
comp.VerifyEmitDiagnostics();
CompileAndVerify(comp, expectedOutput: "1 2");
}
[Fact]
......@@ -1173,7 +1171,7 @@ public void Deconstruct(out int x1, out int x2)
comp.VerifyDiagnostics(
// (7,17): error CS8179: Predefined type 'System.ValueTuple`2' is not defined or imported
// var y = (x, x) = new C();
Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(x, x) = new C()").WithArguments("System.ValueTuple`2").WithLocation(7, 17)
Diagnostic(ErrorCode.ERR_PredefinedValueTupleTypeNotFound, "(x, x)").WithArguments("System.ValueTuple`2").WithLocation(7, 17)
);
}
......@@ -1336,19 +1334,7 @@ static void Main()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x5, var (x6, x7))").WithArguments("tuples", "7").WithLocation(8, 18),
// (9,14): error CS8059: Feature 'tuples' is not available in C# 6. Please use language version 7 or greater.
// for ((int x8, var (x9, x10)) = Pair.Create(1, Pair.Create(2, 3)); ; ) { }
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x8, var (x9, x10))").WithArguments("tuples", "7").WithLocation(9, 14),
// (8,32): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x6'.
// foreach ((int x5, var (x6, x7)) in new[] { Pair.Create(1, Pair.Create(2, 3)) }) { }
Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x6").WithArguments("x6").WithLocation(8, 32),
// (8,36): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x7'.
// foreach ((int x5, var (x6, x7)) in new[] { Pair.Create(1, Pair.Create(2, 3)) }) { }
Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x7").WithArguments("x7").WithLocation(8, 36),
// (9,28): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x9'.
// for ((int x8, var (x9, x10)) = Pair.Create(1, Pair.Create(2, 3)); ; ) { }
Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x9").WithArguments("x9").WithLocation(9, 28),
// (9,32): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x10'.
// for ((int x8, var (x9, x10)) = Pair.Create(1, Pair.Create(2, 3)); ; ) { }
Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x10").WithArguments("x10").WithLocation(9, 32)
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion6, "(int x8, var (x9, x10))").WithArguments("tuples", "7").WithLocation(9, 14)
);
}
......@@ -2700,6 +2686,9 @@ public void Deconstruct(out dynamic x, out dynamic y)
// (6,15): error CS0103: The name 'x1' does not exist in the current context
// (int* x1, int y1) = c;
Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 15),
// (6,19): error CS0266: Cannot implicitly convert type 'dynamic' to 'int'. An explicit conversion exists (are you missing a cast?)
// (int* x1, int y1) = c;
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int y1").WithArguments("dynamic", "int").WithLocation(6, 19),
// (6,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side.
// (int* x1, int y1) = c;
Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(int* x1, int y1)").WithLocation(6, 9),
......@@ -2709,6 +2698,9 @@ public void Deconstruct(out dynamic x, out dynamic y)
// (7,15): error CS0103: The name 'x2' does not exist in the current context
// (var* x2, int y2) = c;
Diagnostic(ErrorCode.ERR_NameNotInContext, "x2").WithArguments("x2").WithLocation(7, 15),
// (7,19): error CS0266: Cannot implicitly convert type 'dynamic' to 'int'. An explicit conversion exists (are you missing a cast?)
// (var* x2, int y2) = c;
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "int y2").WithArguments("dynamic", "int").WithLocation(7, 19),
// (7,9): error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side.
// (var* x2, int y2) = c;
Diagnostic(ErrorCode.ERR_MixedDeconstructionUnsupported, "(var* x2, int y2)").WithLocation(7, 9),
......
......@@ -1517,7 +1517,7 @@ public void TestDeconstruction()
Diagnostic(ErrorCode.ERR_GenericsUsedInNoPIAType, "(1, 2)").WithArguments("System.ValueTuple<T1, T2>").WithLocation(14, 16),
// (19,9): error CS1768: Type 'ValueTuple<T1, T2>' cannot be embedded because it has a generic argument. Consider setting the 'Embed Interop Types' property to false.
// (x, y) = new C();
Diagnostic(ErrorCode.ERR_GenericsUsedInNoPIAType, "(x, y) = new C()").WithArguments("System.ValueTuple<T1, T2>").WithLocation(19, 9)
Diagnostic(ErrorCode.ERR_GenericsUsedInNoPIAType, "(x, y)").WithArguments("System.ValueTuple<T1, T2>").WithLocation(19, 9)
};
var comp1 = CreateCompilationWithMscorlib(source, options: TestOptions.ReleaseDll,
......
......@@ -119,11 +119,9 @@ void Test()
testData.GetMethodData("<>x.<>m0(C)").VerifyIL(@"
{
// Code size 112 (0x70)
// Code size 92 (0x5c)
.maxstack 4
.locals init (System.Guid V_0,
int V_1,
string V_2)
.locals init (System.Guid V_0)
IL_0000: ldtoken ""int""
IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_000a: ldstr ""z1""
......@@ -140,26 +138,18 @@ .maxstack 4
IL_0035: ldloc.0
IL_0036: ldnull
IL_0037: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])""
IL_003c: ldc.i4.1
IL_003d: ldnull
IL_003e: newobj ""System.ValueTuple<int, string>..ctor(int, string)""
IL_0043: dup
IL_0044: ldfld ""int System.ValueTuple<int, string>.Item1""
IL_0049: stloc.1
IL_004a: ldfld ""string System.ValueTuple<int, string>.Item2""
IL_004f: stloc.2
IL_0050: ldstr ""z1""
IL_0055: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<int>(string)""
IL_005a: ldloc.1
IL_005b: stind.i4
IL_005c: ldstr ""z2""
IL_0061: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<string>(string)""
IL_0066: ldloc.2
IL_0067: stind.ref
IL_0068: ldloc.1
IL_0069: ldloc.2
IL_006a: newobj ""System.ValueTuple<int, string>..ctor(int, string)""
IL_006f: ret
IL_003c: ldstr ""z1""
IL_0041: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<int>(string)""
IL_0046: ldc.i4.1
IL_0047: stind.i4
IL_0048: ldstr ""z2""
IL_004d: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<string>(string)""
IL_0052: ldnull
IL_0053: stind.ref
IL_0054: ldc.i4.1
IL_0055: ldnull
IL_0056: newobj ""System.ValueTuple<int, string>..ctor(int, string)""
IL_005b: ret
}");
});
}
......@@ -206,11 +196,9 @@ void Test()
testData.GetMethodData("<>x.<>m0(C)").VerifyIL(@"
{
// Code size 70 (0x46)
// Code size 50 (0x32)
.maxstack 4
.locals init (System.Guid V_0,
int V_1,
string V_2)
.locals init (System.Guid V_0)
IL_0000: ldtoken ""string""
IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_000a: ldstr ""z2""
......@@ -219,22 +207,14 @@ .maxstack 4
IL_0017: ldloc.0
IL_0018: ldnull
IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])""
IL_001e: ldc.i4.1
IL_001f: ldnull
IL_0020: newobj ""System.ValueTuple<int, string>..ctor(int, string)""
IL_0025: dup
IL_0026: ldfld ""int System.ValueTuple<int, string>.Item1""
IL_002b: stloc.1
IL_002c: ldfld ""string System.ValueTuple<int, string>.Item2""
IL_0031: stloc.2
IL_0032: ldstr ""z2""
IL_0037: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<string>(string)""
IL_003c: ldloc.2
IL_003d: stind.ref
IL_003e: ldloc.1
IL_003f: ldloc.2
IL_0040: newobj ""System.ValueTuple<int, string>..ctor(int, string)""
IL_0045: ret
IL_001e: ldstr ""z2""
IL_0023: call ""string Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<string>(string)""
IL_0028: ldnull
IL_0029: stind.ref
IL_002a: ldc.i4.1
IL_002b: ldnull
IL_002c: newobj ""System.ValueTuple<int, string>..ctor(int, string)""
IL_0031: ret
}
");
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册