提交 7953c184 编写于 作者: A AlekseyTs

Expand the lifetime of locals declared within CasePatternSwitchLabelSyntax to...

Expand the lifetime of locals declared within CasePatternSwitchLabelSyntax to the entire switch body.

Fixes #15536.
Fixes #16066.
上级 04f298c2
......@@ -671,6 +671,18 @@
<Field Name="LocalFunctions" Type="ImmutableArray&lt;LocalFunctionSymbol&gt;"/>
</Node>
<!--
This node is used to represent a visibility scope for locals,
for which lifetime is extended beyond the visibility scope.
At the moment, only locals declared within CasePatternSwitchLabelSyntax
have their lifetime expanded to the entire switch body, whereas their
visibility scope is the declaring switch section. The node is added into
the tree during lowering phase.
-->
<Node Name="BoundScope" Base="BoundStatementList">
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
</Node>
<!--
BoundStateMachineScope represents a scope within a translated iterator/async method.
It is used to emit debugging information that allows the EE to map
......
......@@ -24,6 +24,10 @@ private void EmitStatement(BoundStatement statement)
EmitBlock((BoundBlock)statement);
break;
case BoundKind.Scope:
EmitScope((BoundScope)statement);
break;
case BoundKind.SequencePoint:
this.EmitSequencePointStatement((BoundSequencePoint)statement);
break;
......@@ -609,10 +613,7 @@ private void EmitBlock(BoundBlock block)
}
}
foreach (var statement in block.Statements)
{
EmitStatement(statement);
}
EmitStatements(block.Statements);
if (_indirectReturnState == IndirectReturnState.Needed &&
IsLastBlockInMethod(block))
......@@ -631,6 +632,36 @@ private void EmitBlock(BoundBlock block)
}
}
private void EmitStatements(ImmutableArray<BoundStatement> statements)
{
foreach (var statement in statements)
{
EmitStatement(statement);
}
}
private void EmitScope(BoundScope block)
{
Debug.Assert(!block.Locals.IsEmpty);
_builder.OpenLocalScope();
foreach (var local in block.Locals)
{
Debug.Assert(local.Name != null);
Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.UserDefined &&
local.ScopeDesignatorOpt?.Kind() == SyntaxKind.SwitchSection);
if (!local.IsConst && !IsStackLocal(local))
{
_builder.AddLocalToScope(_builder.LocalSlotManager.GetLocal(local));
}
}
EmitStatements(block.Statements);
_builder.CloseLocalScope();
}
private void EmitStateMachineScope(BoundStateMachineScope scope)
{
_builder.OpenStateMachineScope();
......@@ -1464,7 +1495,9 @@ private LocalDefinition DefineLocal(LocalSymbol local, SyntaxNode syntaxNode)
isSlotReusable: local.SynthesizedKind.IsSlotReusable(_ilEmitStyle != ILEmitStyle.Release));
// If named, add it to the local debug scope.
if (localDef.Name != null)
if (localDef.Name != null &&
!(local.SynthesizedKind == SynthesizedLocalKind.UserDefined &&
local.ScopeDesignatorOpt?.Kind() == SyntaxKind.SwitchSection)) // Visibility scope of such locals is represented by BoundScope node.
{
_builder.AddLocalToScope(localDef);
}
......
......@@ -2082,6 +2082,11 @@ internal override SynthesizedLocalKind SynthesizedKind
get { return SynthesizedLocalKind.OptimizerTemp; }
}
internal override SyntaxNode ScopeDesignatorOpt
{
get { return null; }
}
internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax)
{
throw new NotImplementedException();
......
......@@ -1144,10 +1144,21 @@ public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node)
public override BoundNode VisitBlock(BoundBlock node)
{
foreach (var statement in node.Statements)
VisitStatements(node.Statements);
return null;
}
private void VisitStatements(ImmutableArray<BoundStatement> statements)
{
foreach (var statement in statements)
{
VisitStatement(statement);
}
}
public override BoundNode VisitScope(BoundScope node)
{
VisitStatements(node.Statements);
return null;
}
......
......@@ -70,6 +70,11 @@ private static string GetCapturedVariableFieldName(Symbol variable, ref int uniq
{
return GeneratedNames.MakeSynthesizedInstrumentationPayloadLocalFieldName(uniqueId++);
}
if (local.SynthesizedKind == SynthesizedLocalKind.UserDefined && local.ScopeDesignatorOpt?.Kind() == SyntaxKind.SwitchSection)
{
return GeneratedNames.MakeHoistedLocalFieldName(local.SynthesizedKind, uniqueId++, local.Name);
}
}
Debug.Assert(variable.Name != null);
......
......@@ -1002,6 +1002,22 @@ private BoundBlock RewriteBlock(BoundBlock node, ArrayBuilder<BoundExpression> p
return node.Update(newLocals.ToImmutableAndFree(), node.LocalFunctions, newStatements.ToImmutableAndFree());
}
public override BoundNode VisitScope(BoundScope node)
{
Debug.Assert(!node.Locals.IsEmpty);
var newLocals = ArrayBuilder<LocalSymbol>.GetInstance();
RewriteLocals(node.Locals, newLocals);
var statements = VisitList(node.Statements);
if (newLocals.Count == 0)
{
newLocals.Free();
return new BoundStatementList(node.Syntax, statements);
}
return node.Update(newLocals.ToImmutableAndFree(), statements);
}
public override BoundNode VisitCatchBlock(BoundCatchBlock node)
{
// Test if this frame has captured variables and requires the introduction of a closure class.
......
......@@ -82,9 +82,15 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node)
}
// at this point the end of result is unreachable.
_declaredTemps.AddOptional(initialTemp);
_declaredTemps.AddRange(node.InnerLocals);
// output the sections of code
foreach (var section in node.SwitchSections)
{
// Lifetime of these locals is expanded to the entire switch body.
_declaredTemps.AddRange(section.Locals);
// Start with the part of the decision tree that is in scope of the section variables.
// Its endpoint is not reachable (it jumps back into the decision tree code).
var sectionSyntax = (SyntaxNode)section.Syntax;
......@@ -98,14 +104,23 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node)
// Add the translated body of the switch section
sectionBuilder.AddRange(_localRewriter.VisitList(section.Statements));
sectionBuilder.Add(_factory.Goto(node.BreakLabel));
result.Add(_factory.Block(section.Locals, sectionBuilder.ToImmutableAndFree()));
ImmutableArray<BoundStatement> statements = sectionBuilder.ToImmutableAndFree();
if (section.Locals.IsEmpty)
{
result.Add(_factory.StatementList(statements));
}
else
{
result.Add(new BoundScope(section.Syntax, section.Locals, statements));
}
// at this point the end of result is unreachable.
}
result.Add(_factory.Label(node.BreakLabel));
_declaredTemps.AddOptional(initialTemp);
_declaredTemps.AddRange(node.InnerLocals);
BoundStatement translatedSwitch = _factory.Block(_declaredTemps.ToImmutableArray(), node.InnerLocalFunctions, result.ToImmutableAndFree());
// Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
......
......@@ -138,6 +138,8 @@ public override BoundNode VisitBlock(BoundBlock node)
return node.Update(newLocals, newLocalFunctions, newStatements);
}
public override abstract BoundNode VisitScope(BoundScope node);
public override BoundNode VisitSequence(BoundSequence node)
{
var newLocals = RewriteLocals(node.Locals);
......
......@@ -292,7 +292,8 @@ private BoundStatement PossibleIteratorScope(ImmutableArray<LocalSymbol> locals,
// We need to produce hoisted local scope debug information for user locals as well as
// lambda display classes, since Dev12 EE uses them to determine which variables are displayed
// in Locals window.
if (local.SynthesizedKind == SynthesizedLocalKind.UserDefined ||
if ((local.SynthesizedKind == SynthesizedLocalKind.UserDefined &&
local.ScopeDesignatorOpt?.Kind() != SyntaxKind.SwitchSection) ||
local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass)
{
hoistedLocalsWithDebugScopes.Add(((CapturedToStateMachineFieldReplacement)proxy).HoistedField);
......@@ -621,6 +622,52 @@ public override BoundNode VisitBlock(BoundBlock node)
return PossibleIteratorScope(node.Locals, () => (BoundStatement)base.VisitBlock(node));
}
public override BoundNode VisitScope(BoundScope node)
{
Debug.Assert(!node.Locals.IsEmpty);
var newLocals = ArrayBuilder<LocalSymbol>.GetInstance();
var hoistedLocalsWithDebugScopes = ArrayBuilder<StateMachineFieldSymbol>.GetInstance();
foreach (var local in node.Locals)
{
Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.UserDefined &&
local.ScopeDesignatorOpt?.Kind() == SyntaxKind.SwitchSection);
if (!NeedsProxy(local))
{
newLocals.Add(local);
continue;
}
hoistedLocalsWithDebugScopes.Add(((CapturedToStateMachineFieldReplacement)proxies[local]).HoistedField);
}
var statements = VisitList(node.Statements);
// wrap the node in an iterator scope for debugging
if (hoistedLocalsWithDebugScopes.Count != 0)
{
BoundStatement translated;
if (newLocals.Count == 0)
{
newLocals.Free();
translated = new BoundStatementList(node.Syntax, statements);
}
else
{
translated = node.Update(newLocals.ToImmutableAndFree(), statements);
}
return MakeStateMachineScope(hoistedLocalsWithDebugScopes.ToImmutable(), translated);
}
else
{
hoistedLocalsWithDebugScopes.Free();
newLocals.Free();
return node.Update(node.Locals, statements);
}
}
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
return PossibleIteratorScope(node.InnerLocals, () => (BoundStatement)base.VisitSwitchStatement(node));
......
......@@ -25,6 +25,11 @@ protected LocalSymbol()
get;
}
/// <summary>
/// Syntax node that is used as the scope designator. Otherwise, null.
/// </summary>
internal abstract SyntaxNode ScopeDesignatorOpt { get; }
internal abstract LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax);
internal abstract bool IsImportedFromMetadata
......
......@@ -76,6 +76,11 @@ internal Binder ScopeBinder
get { return _scopeBinder; }
}
internal override SyntaxNode ScopeDesignatorOpt
{
get { return _scopeBinder.ScopeDesignator; }
}
/// <summary>
/// Binder that should be used to bind type syntax for the local.
/// </summary>
......
......@@ -102,6 +102,11 @@ internal override SynthesizedLocalKind SynthesizedKind
get { return _kind; }
}
internal override SyntaxNode ScopeDesignatorOpt
{
get { return null; }
}
internal override SyntaxToken IdentifierToken
{
get { return default(SyntaxToken); }
......
......@@ -38,6 +38,11 @@ internal override SynthesizedLocalKind SynthesizedKind
get { return _originalVariable.SynthesizedKind; }
}
internal override SyntaxNode ScopeDesignatorOpt
{
get { return _originalVariable.ScopeDesignatorOpt; }
}
public override string Name
{
get { return _originalVariable.Name; }
......
......@@ -2150,7 +2150,7 @@ static void Main(string[] args)
}
[Fact]
public void SwitchWithPattern()
public void SwitchWithPattern_01()
{
string source = @"
using System;
......@@ -2203,11 +2203,11 @@ class Student : Person { public double GPA; }
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""0"" offset=""59"" />
<slot kind=""21"" offset=""0"" />
<slot kind=""0"" offset=""163"" />
<slot kind=""0"" offset=""250"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
......@@ -2226,13 +2226,109 @@ class Student : Person { public double GPA; }
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xcc"">
<scope startOffset=""0x36"" endOffset=""0x6e"">
<local name=""s"" il_index=""6"" il_start=""0x36"" il_end=""0x6e"" attributes=""0"" />
<local name=""s"" il_index=""5"" il_start=""0x36"" il_end=""0x6e"" attributes=""0"" />
</scope>
<scope startOffset=""0x6e"" endOffset=""0x94"">
<local name=""s"" il_index=""8"" il_start=""0x6e"" il_end=""0x94"" attributes=""0"" />
<local name=""s"" il_index=""6"" il_start=""0x6e"" il_end=""0x94"" attributes=""0"" />
</scope>
<scope startOffset=""0x94"" endOffset=""0xb5"">
<local name=""t"" il_index=""9"" il_start=""0x94"" il_end=""0xb5"" attributes=""0"" />
<local name=""t"" il_index=""7"" il_start=""0x94"" il_end=""0xb5"" attributes=""0"" />
</scope>
</scope>
</method>
</methods>
</symbols>"
);
}
[Fact]
public void SwitchWithPattern_02()
{
string source = @"
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
private static List<List<int>> l = new List<List<int>>();
static void Main(string[] args)
{
Student s = new Student();
s.Name = ""Bozo"";
s.GPA = 2.3;
Operate(s);
}
static System.Func<string> Operate(Person p)
{
switch (p)
{
case Student s when s.GPA > 3.5:
return () => $""Student {s.Name} ({s.GPA:N1})"";
case Student s:
return () => $""Student {s.Name} ({s.GPA:N1})"";
case Teacher t:
return () => $""Teacher {t.Name} of {t.Subject}"";
default:
return () => $""Person {p.Name}"";
}
}
}
class Person { public string Name; }
class Teacher : Person { public string Subject; }
class Student : Person { public double GPA; }
";
// we just want this to compile without crashing/asserting
var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll);
c.VerifyPdb("Program.Operate",
@"<symbols>
<methods>
<method containingType=""Program"" name=""Operate"" parameterNames=""p"">
<customDebugInfo>
<forward declaringType=""Program"" methodName=""Main"" parameterNames=""args"" />
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""383"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
<encLambdaMap>
<methodOrdinal>2</methodOrdinal>
<closure offset=""383"" />
<closure offset=""0"" />
<lambda offset=""109"" closure=""0"" />
<lambda offset=""202"" closure=""0"" />
<lambda offset=""295"" closure=""0"" />
<lambda offset=""383"" closure=""1"" />
</encLambdaMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" hidden=""true"" />
<entry offset=""0xd"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" />
<entry offset=""0xe"" hidden=""true"" />
<entry offset=""0x20"" hidden=""true"" />
<entry offset=""0x50"" hidden=""true"" />
<entry offset=""0x57"" startLine=""22"" startColumn=""28"" endLine=""22"" endColumn=""44"" />
<entry offset=""0x6f"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""63"" />
<entry offset=""0x7f"" hidden=""true"" />
<entry offset=""0x89"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""63"" />
<entry offset=""0x99"" hidden=""true"" />
<entry offset=""0xa3"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""65"" />
<entry offset=""0xb3"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""49"" />
<entry offset=""0xc3"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xc6"">
<local name=""CS$&lt;&gt;8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xc6"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xc3"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xc3"" attributes=""0"" />
</scope>
</scope>
</method>
......
......@@ -2375,5 +2375,174 @@ public static void Main(string[] args)
Assert.Equal(symbol, model.GetSymbolInfo(refs[0]).Symbol);
});
}
[Fact]
[WorkItem(15536, "https://github.com/dotnet/roslyn/issues/15536")]
public void CallFromDifferentSwitchSection_01()
{
var source = @"
class Program
{
static void Main()
{
Test(string.Empty);
}
static void Test(object o)
{
switch (o)
{
case string x:
Assign();
Print();
break;
case int x:
void Assign() { x = 5; }
void Print() => System.Console.WriteLine(x);
break;
}
}
}";
var comp = CreateCompilationWithMscorlib46(source, parseOptions: DefaultParseOptions, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "5");
}
[Fact]
[WorkItem(15536, "https://github.com/dotnet/roslyn/issues/15536")]
public void CallFromDifferentSwitchSection_02()
{
var source = @"
class Program
{
static void Main()
{
Test(string.Empty);
}
static void Test(object o)
{
switch (o)
{
case int x:
void Assign() { x = 5; }
void Print() => System.Console.WriteLine(x);
break;
case string x:
Assign();
Print();
break;
}
}
}";
var comp = CreateCompilationWithMscorlib46(source, parseOptions: DefaultParseOptions, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "5");
}
[Fact]
[WorkItem(15536, "https://github.com/dotnet/roslyn/issues/15536")]
public void CallFromDifferentSwitchSection_03()
{
var source = @"
class Program
{
static void Main()
{
Test(string.Empty);
}
static void Test(object o)
{
switch (o)
{
case string x:
Assign();
System.Action p = Print;
p();
break;
case int x:
void Assign() { x = 5; }
void Print() => System.Console.WriteLine(x);
break;
}
}
}";
var comp = CreateCompilationWithMscorlib46(source, parseOptions: DefaultParseOptions, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "5");
}
[Fact]
[WorkItem(15536, "https://github.com/dotnet/roslyn/issues/15536")]
public void CallFromDifferentSwitchSection_04()
{
var source = @"
class Program
{
static void Main()
{
Test(string.Empty);
}
static void Test(object o)
{
switch (o)
{
case int x:
void Assign() { x = 5; }
void Print() => System.Console.WriteLine(x);
break;
case string x:
Assign();
System.Action p = Print;
p();
break;
}
}
}";
var comp = CreateCompilationWithMscorlib46(source, parseOptions: DefaultParseOptions, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "5");
}
[Fact]
[WorkItem(15536, "https://github.com/dotnet/roslyn/issues/15536")]
public void CallFromDifferentSwitchSection_05()
{
var source = @"
class Program
{
static void Main()
{
Test(string.Empty);
}
static void Test(object o)
{
switch (o)
{
case string x:
Local1();
break;
case int x:
void Local1() => Local2(x = 5);
break;
case char x:
void Local2(int y)
{
System.Console.WriteLine(x = 'a');
System.Console.WriteLine(y);
}
break;
}
}
}";
var comp = CreateCompilationWithMscorlib46(source, parseOptions: DefaultParseOptions, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput:
@"a
5");
}
}
}
......@@ -1997,5 +1997,547 @@ static void Main()
@"not null
null");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithLambda_01()
{
var source =
@"
class Program
{
static void Main(string[] args)
{
switch((object)new A())
{
case A a:
System.Action print = () => System.Console.WriteLine(a);
print();
break;
}
}
}
class A{}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var expectedOutput = "A";
var comp = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithLambda_02()
{
var source =
@"
class Program
{
static void Main(string[] args)
{
int val = 3;
var l = new System.Collections.Generic.List<System.Action>();
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
System.Console.WriteLine(""case 1: {0}"", i);
a = i++;
l.Add(() => System.Console.WriteLine(a));
goto case 2;
case int a when a == 4:
case 2:
System.Console.WriteLine(""case 2: {0}"", i);
a = i++;
l.Add(() => System.Console.WriteLine(a));
if (i < 4)
{
goto case 1;
}
break;
}
foreach (var a in l)
{
a();
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
case 2: 2
case 1: 3
case 2: 4
3
4
3
4");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithYield_01()
{
var source =
@"
class Program
{
static void Main()
{
foreach (var a in Test())
{
System.Console.WriteLine(a);
}
}
static System.Collections.Generic.IEnumerable<string> Test()
{
int val = 3;
var l = new System.Collections.Generic.List<System.Action>();
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
yield return string.Format(""case 1: {0}"", i);
a = i++;
l.Add(() => System.Console.WriteLine(a));
goto case 2;
case int a when a == 4:
case 2:
yield return string.Format(""case 2: {0}"", i);
a = i++;
l.Add(() => System.Console.WriteLine(a));
if (i < 4)
{
goto case 1;
}
break;
}
foreach (var a in l)
{
a();
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
case 2: 2
case 1: 3
case 2: 4
3
4
3
4");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithYield_02()
{
var source =
@"
class Program
{
static void Main()
{
foreach (var a in Test())
{
System.Console.WriteLine(a);
}
}
static System.Collections.Generic.IEnumerable<string> Test()
{
int val = 3;
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
yield return string.Format(""case 1: {0}"", i);
a = i++;
System.Console.WriteLine(a);
goto case 2;
case int a when a == 4:
case 2:
yield return string.Format(""case 2: {0}"", i);
a = i++;
System.Console.WriteLine(a);
if (i < 4)
{
goto case 1;
}
break;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
1
case 2: 2
2
case 1: 3
3
case 2: 4
4");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithYield_03()
{
var source =
@"
class Program
{
static void Main()
{
foreach (var a in Test())
{
System.Console.WriteLine(a);
}
}
static System.Collections.Generic.IEnumerable<int> Test()
{
int val = 3;
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
System.Console.WriteLine(""case 1: {0}"", i);
a = i++;
System.Console.WriteLine(a);
goto case 2;
case int a when a == 4:
case 2:
System.Console.WriteLine(""case 2: {0}"", i);
a = i++;
System.Console.WriteLine(a);
if (i < 4)
{
goto case 1;
}
break;
}
yield return i;
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
1
case 2: 2
2
case 1: 3
3
case 2: 4
4
5");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithYield_04()
{
var source =
@"
class Program
{
static void Main()
{
foreach (var a in Test())
{
System.Console.WriteLine(a);
}
}
static System.Collections.Generic.IEnumerable<string> Test()
{
int val = 3;
switch(val)
{
case int a when TakeOutVar(out var b) && a == b:
yield return string.Format(""case: {0}"", val);
System.Console.WriteLine(a);
break;
}
}
static bool TakeOutVar(out int x)
{
x = 3;
return true;
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case: 3
3");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithAwait_01()
{
var source =
@"
using System.Threading.Tasks;
class Program
{
static void Main()
{
System.Console.WriteLine(Test().Result);
}
static async Task<int> Test()
{
int val = 3;
var l = new System.Collections.Generic.List<System.Action>();
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
System.Console.WriteLine(await GetTask(string.Format(""case 1: {0}"", i)));
a = i++;
l.Add(() => System.Console.WriteLine(a));
goto case 2;
case int a when a == 4:
case 2:
System.Console.WriteLine(await GetTask(string.Format(""case 2: {0}"", i)));
a = i++;
l.Add(() => System.Console.WriteLine(a));
if (i < 4)
{
goto case 1;
}
break;
}
foreach (var a in l)
{
a();
}
return i;
}
static async Task<string> GetTask(string val)
{
await Task.Yield();
return val;
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
case 2: 2
case 1: 3
case 2: 4
3
4
3
4
5");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithAwait_02()
{
var source =
@"
using System.Threading.Tasks;
class Program
{
static void Main()
{
System.Console.WriteLine(Test().Result);
}
static async Task<int> Test()
{
int val = 3;
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
System.Console.WriteLine(await GetTask(string.Format(""case 1: {0}"", i)));
a = i++;
System.Console.WriteLine(a);
goto case 2;
case int a when a == 4:
case 2:
System.Console.WriteLine(await GetTask(string.Format(""case 2: {0}"", i)));
a = i++;
System.Console.WriteLine(a);
if (i < 4)
{
goto case 1;
}
break;
}
return i;
}
static async Task<string> GetTask(string val)
{
await Task.Yield();
return val;
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
1
case 2: 2
2
case 1: 3
3
case 2: 4
4
5");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithAwait_03()
{
var source =
@"
using System.Threading.Tasks;
class Program
{
static void Main()
{
System.Console.WriteLine(Test().Result);
}
static async Task<int> Test()
{
int val = 3;
int i = 1;
switch(val)
{
case int a when a == 3:
case 1:
System.Console.WriteLine(""case 1: {0}"", i);
a = i++;
System.Console.WriteLine(a);
goto case 2;
case int a when a == 4:
case 2:
System.Console.WriteLine(""case 2: {0}"", i);
a = i++;
System.Console.WriteLine(a);
if (i < 4)
{
goto case 1;
}
break;
}
await Task.Yield();
return i;
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case 1: 1
1
case 2: 2
2
case 1: 3
3
case 2: 4
4
5");
}
[Fact]
[WorkItem(16066, "https://github.com/dotnet/roslyn/issues/16066")]
public void SwitchSectionWithAwait_04()
{
var source =
@"
using System.Threading.Tasks;
class Program
{
static void Main()
{
System.Console.WriteLine(Test().Result);
}
static async Task<int> Test()
{
int val = 3;
switch(val)
{
case int a when TakeOutVar(out var b) && a == b:
System.Console.WriteLine(await GetTask(string.Format(""case: {0}"", val)));
return a;
}
return 0;
}
static bool TakeOutVar(out int x)
{
x = 3;
return true;
}
static async Task<string> GetTask(string val)
{
await Task.Yield();
return val;
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics();
var comp = CompileAndVerify(compilation, expectedOutput:
@"case: 3
3");
}
}
}
......@@ -43,6 +43,11 @@ internal sealed override SynthesizedLocalKind SynthesizedKind
get { return SynthesizedLocalKind.UserDefined; }
}
internal override SyntaxNode ScopeDesignatorOpt
{
get { return null; }
}
internal sealed override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax)
{
throw ExceptionUtilities.Unreachable;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册