Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
cec28aa3
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
cec28aa3
编写于
4月 01, 2018
作者:
N
Neal Gafter
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Refactoring in prep for tuple literal switch optimization
上级
ab7a2446
变更
14
展开全部
隐藏空白更改
内联
并排
Showing
14 changed file
with
995 addition
and
886 deletion
+995
-886
src/Compilers/CSharp/Portable/BoundTree/BoundDagTemp.cs
src/Compilers/CSharp/Portable/BoundTree/BoundDagTemp.cs
+5
-0
src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs
src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs
+1
-1
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
...lRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
+139
-48
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs
...ing/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs
+22
-35
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs
...Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs
+96
-41
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
.../Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
+32
-31
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
+145
-147
src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
+325
-323
src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
...p/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
+140
-166
src/Compilers/CSharp/Test/Emit/PDB/PDBLambdaTests.cs
src/Compilers/CSharp/Test/Emit/PDB/PDBLambdaTests.cs
+2
-3
src/Compilers/CSharp/Test/Emit/PDB/PDBLocalFunctionTests.cs
src/Compilers/CSharp/Test/Emit/PDB/PDBLocalFunctionTests.cs
+1
-1
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
+81
-84
src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs
...rs/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs
+3
-3
src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs
...pilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs
+3
-3
未找到文件。
src/Compilers/CSharp/Portable/BoundTree/BoundDagTemp.cs
浏览文件 @
cec28aa3
...
...
@@ -6,6 +6,11 @@ namespace Microsoft.CodeAnalysis.CSharp
{
partial
class
BoundDagTemp
{
/// <summary>
/// Does this dag temp represent the original input of the pattern-matching operation?
/// </summary>
public
bool
IsOriginalInput
=>
this
.
Source
==
null
;
public
override
bool
Equals
(
object
obj
)
=>
obj
is
BoundDagTemp
other
&&
this
.
Equals
(
other
);
public
bool
Equals
(
BoundDagTemp
other
)
{
...
...
src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs
浏览文件 @
cec28aa3
...
...
@@ -156,7 +156,7 @@ BoundDecisionDagNode makeReplacement(BoundDecisionDagNode dag)
// Is the decision's result known because the input is a constant?
bool
?
knownResult
(
BoundDagTest
choice
)
{
if
(
choice
.
Input
.
Source
!=
null
)
if
(
!
choice
.
Input
.
IsOriginalInput
)
{
// This is a test of something other than the main input; result unknown
return
null
;
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
浏览文件 @
cec28aa3
...
...
@@ -24,57 +24,44 @@ private class BasePatternSwitchLocalRewriter : PatternLocalRewriter
/// includes the code to assign to the pattern variables and evaluate the when clause. Since a
/// when clause can yield a false value, it can jump back to a label in the lowered decision dag.
/// </summary>
protected
readonly
Dictionary
<
SyntaxNode
,
ArrayBuilder
<
BoundStatement
>>
_switchArms
=
new
Dictionary
<
SyntaxNode
,
ArrayBuilder
<
BoundStatement
>>();
/// <summary>
/// In a switch expression, some labels may first reached by a backward branch, and
/// it may occur when something (from the enclosing expression) is on the stack.
/// To satisfy the verifier, the caller must arrange forward jumps to these labels. The set of
/// states whose labels will need such forward jumps (if something can be on the stack) is stored in
/// _backwardLabels. In practice, this is exclusively the set of states that are reached
/// when a when-clause evaluates to false.
/// PROTOTYPE(patterns2): This is a placeholder. It is not used yet for lowering the
/// switch expression, where it will be needed.
/// </summary>
private
readonly
ArrayBuilder
<
BoundDecisionDagNode
>
_backwardLabels
=
ArrayBuilder
<
BoundDecisionDagNode
>.
GetInstance
();
private
readonly
PooledDictionary
<
SyntaxNode
,
ArrayBuilder
<
BoundStatement
>>
_switchArms
=
PooledDictionary
<
SyntaxNode
,
ArrayBuilder
<
BoundStatement
>>.
GetInstance
();
/// <summary>
/// The lowered decision dag. This includes all of the code to decide which pattern
/// is matched, but not the code to assign to pattern variables and evaluate when clauses.
/// </summary>
pr
otected
readonly
ArrayBuilder
<
BoundStatement
>
_loweredDecisionDag
=
ArrayBuilder
<
BoundStatement
>.
GetInstance
();
pr
ivate
readonly
ArrayBuilder
<
BoundStatement
>
_loweredDecisionDag
=
ArrayBuilder
<
BoundStatement
>.
GetInstance
();
/// <summary>
/// The label in the code for the beginning of code for each node of the dag.
/// </summary>
private
readonly
Dictionary
<
BoundDecisionDagNode
,
LabelSymbol
>
_dagNodeLabels
=
new
Dictionary
<
BoundDecisionDagNode
,
LabelSymbol
>
();
private
readonly
PooledDictionary
<
BoundDecisionDagNode
,
LabelSymbol
>
_dagNodeLabels
=
PooledDictionary
<
BoundDecisionDagNode
,
LabelSymbol
>.
GetInstance
();
protected
BasePatternSwitchLocalRewriter
(
LocalRewriter
localRewriter
,
BoundExpression
loweredInput
,
ImmutableArray
<
SyntaxNode
>
arms
,
BoundDecisionDag
decisionDag
)
:
base
(
localRewriter
,
loweredInput
)
/// <summary>
/// True if we are translating a switch statement. This affects sequence points (a when clause gets
/// a sequence point in a switch statement, but not in a switch expression).
/// </summary>
private
readonly
bool
_isSwitchStatement
;
protected
BasePatternSwitchLocalRewriter
(
SyntaxNode
node
,
LocalRewriter
localRewriter
,
ImmutableArray
<
SyntaxNode
>
arms
,
bool
isSwitchStatement
)
:
base
(
node
,
localRewriter
)
{
this
.
_isSwitchStatement
=
isSwitchStatement
;
foreach
(
var
arm
in
arms
)
{
_switchArms
.
Add
(
arm
,
new
ArrayBuilder
<
BoundStatement
>
());
_switchArms
.
Add
(
arm
,
ArrayBuilder
<
BoundStatement
>.
GetInstance
());
}
ImmutableArray
<
BoundDecisionDagNode
>
sortedNodes
=
decisionDag
.
TopologicallySortedNodes
;
ComputeLabelSet
(
sortedNodes
);
LowerDecisionDag
(
sortedNodes
);
}
private
void
ComputeLabelSet
(
ImmutableArray
<
BoundDecisionDagNode
>
sortedNodes
)
private
void
ComputeLabelSet
(
BoundDecisionDag
decisionDag
)
{
// Nodes with more than one predecessor are assigned a label
var
hasPredecessor
=
PooledHashSet
<
BoundDecisionDagNode
>.
GetInstance
();
void
notePredecessor
(
BoundDecisionDagNode
successor
)
{
if
(
successor
!=
null
&&
!
hasPredecessor
.
Add
(
successor
))
{
GetDagNodeLabel
(
successor
);
}
}
foreach
(
BoundDecisionDagNode
node
in
sortedNodes
)
foreach
(
BoundDecisionDagNode
node
in
decisionDag
.
TopologicallySortedNodes
)
{
switch
(
node
)
{
...
...
@@ -83,7 +70,6 @@ void notePredecessor(BoundDecisionDagNode successor)
if
(
w
.
WhenFalse
!=
null
)
{
GetDagNodeLabel
(
w
.
WhenFalse
);
_backwardLabels
.
Add
(
w
.
WhenFalse
);
}
break
;
case
BoundLeafDecisionDagNode
d
:
...
...
@@ -103,13 +89,22 @@ void notePredecessor(BoundDecisionDagNode successor)
}
hasPredecessor
.
Free
();
return
;
void
notePredecessor
(
BoundDecisionDagNode
successor
)
{
if
(
successor
!=
null
&&
!
hasPredecessor
.
Add
(
successor
))
{
GetDagNodeLabel
(
successor
);
}
}
}
protected
new
void
Free
()
{
_dagNodeLabels
.
Free
();
_switchArms
.
Free
();
base
.
Free
();
_loweredDecisionDag
.
Free
();
_backwardLabels
.
Free
();
}
protected
virtual
LabelSymbol
GetDagNodeLabel
(
BoundDecisionDagNode
dag
)
...
...
@@ -122,11 +117,115 @@ protected virtual LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
return
label
;
}
/// <summary>
/// A utility class that is used to scan a when clause to determine if it might assign a variable,
/// directly or indirectly. Used to determine if we can skip the allocation of pattern-matching
/// temporary variables and use user-declared variables instead, because we can conclude that they
/// are not mutated while the pattern-matching automaton is running.
/// </summary>
protected
class
WhenClauseMightAssignWalker
:
BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
{
private
readonly
bool
_isSwitchStatement
;
private
bool
_mightAssignSomething
;
internal
WhenClauseMightAssignWalker
(
bool
isSwitchStatement
)
{
this
.
_isSwitchStatement
=
isSwitchStatement
;
}
public
bool
MightAssignSomething
(
BoundExpression
expr
)
{
if
(
expr
==
null
||
expr
.
ConstantValue
!=
null
)
{
return
false
;
}
this
.
_mightAssignSomething
=
false
;
this
.
Visit
(
expr
);
return
this
.
_mightAssignSomething
;
}
public
override
BoundNode
VisitCall
(
BoundCall
node
)
{
_mightAssignSomething
=
// might be a call to a local function that assigns a pattern temp
_isSwitchStatement
&&
node
.
Method
.
MethodKind
==
MethodKind
.
LocalFunction
||
// or perhaps we are passing a variable by ref and mutating it that way
!
node
.
ArgumentRefKindsOpt
.
IsDefault
;
return
base
.
VisitCall
(
node
);
}
public
override
BoundNode
VisitAssignmentOperator
(
BoundAssignmentOperator
node
)
{
_mightAssignSomething
=
true
;
return
null
;
}
public
override
BoundNode
VisitCompoundAssignmentOperator
(
BoundCompoundAssignmentOperator
node
)
{
_mightAssignSomething
=
true
;
return
null
;
}
public
override
BoundNode
VisitDeconstructionAssignmentOperator
(
BoundDeconstructionAssignmentOperator
node
)
{
_mightAssignSomething
=
true
;
return
null
;
}
public
override
BoundNode
VisitObjectCreationExpression
(
BoundObjectCreationExpression
node
)
{
// perhaps we are passing a variable by ref and mutating it that way
_mightAssignSomething
=
!
node
.
ArgumentRefKindsOpt
.
IsDefault
;
return
base
.
VisitObjectCreationExpression
(
node
);
}
}
protected
BoundDecisionDag
ShareTempsIfPossibleAndEvaluateInput
(
BoundDecisionDag
decisionDag
,
BoundExpression
loweredSwitchGoverningExpression
,
ArrayBuilder
<
BoundStatement
>
result
)
{
// Note that a when-clause can contain an assignment to a
// pattern variable declared in a different when-clause (e.g. in the same section, or
// in a different section via the use of a local function), so we need to analyze all
// of the when clauses to see if they are all simple enough to conclude that they do
// not mutate pattern variables.
var
mightAssignWalker
=
new
WhenClauseMightAssignWalker
(
isSwitchStatement
:
this
.
_isSwitchStatement
);
bool
canShareTemps
=
!
decisionDag
.
TopologicallySortedNodes
.
OfType
<
BoundWhenDecisionDagNode
>()
.
Any
(
w
=>
mightAssignWalker
.
MightAssignSomething
(
w
.
WhenExpression
));
if
(
canShareTemps
)
{
return
ShareTempsAndEvaluateInput
(
loweredSwitchGoverningExpression
,
decisionDag
,
expr
=>
result
.
Add
(
_factory
.
ExpressionStatement
(
expr
)));
}
else
{
// assign the input expression to its temp.
BoundExpression
inputTemp
=
_tempAllocator
.
GetTemp
(
InputTemp
(
loweredSwitchGoverningExpression
));
Debug
.
Assert
(
inputTemp
!=
loweredSwitchGoverningExpression
);
result
.
Add
(
_factory
.
Assignment
(
inputTemp
,
loweredSwitchGoverningExpression
));
return
decisionDag
;
}
}
/// <summary>
/// Lower the given nodes into _loweredDecisionDag.
/// </summary>
pr
ivate
void
LowerDecisionDag
(
ImmutableArray
<
BoundDecisionDagNode
>
sortedNodes
)
pr
otected
(
ImmutableArray
<
BoundStatement
>
loweredDag
,
ImmutableDictionary
<
SyntaxNode
,
ImmutableArray
<
BoundStatement
>>
switchSections
)
LowerDecisionDag
(
BoundDecisionDag
decisionDag
)
{
Debug
.
Assert
(
this
.
_loweredDecisionDag
.
IsEmpty
());
ComputeLabelSet
(
decisionDag
);
LowerDecisionDagCore
(
decisionDag
);
ImmutableArray
<
BoundStatement
>
loweredDag
=
this
.
_loweredDecisionDag
.
ToImmutableAndFree
();
ImmutableDictionary
<
SyntaxNode
,
ImmutableArray
<
BoundStatement
>>
switchSections
=
this
.
_switchArms
.
ToImmutableDictionary
(
kv
=>
kv
.
Key
,
kv
=>
kv
.
Value
.
ToImmutableAndFree
());
return
(
loweredDag
,
switchSections
);
}
private
void
LowerDecisionDagCore
(
BoundDecisionDag
decisionDag
)
{
ImmutableArray
<
BoundDecisionDagNode
>
sortedNodes
=
decisionDag
.
TopologicallySortedNodes
;
var
firstNode
=
sortedNodes
[
0
];
switch
(
firstNode
)
{
...
...
@@ -138,16 +237,6 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
break
;
}
// Note that a when-clause can contain an assignment to a
// pattern variable declared in a different when-clause (e.g. in the same section, or
// in a different section via the use of a local function), so we only apply the optimization
// of using user-declared pattern variables as pattern-matching automaton temps
// when there are no nontrivial when-clauses at all.
if
(!
sortedNodes
.
Any
(
n
=>
n
is
BoundWhenDecisionDagNode
w
&&
w
.
WhenExpression
!=
null
&&
w
.
WhenExpression
.
ConstantValue
!=
ConstantValue
.
True
))
{
ShareTemps
(
sortedNodes
);
}
// Code for each when clause goes in the separate code section for its switch section.
foreach
(
BoundDecisionDagNode
node
in
sortedNodes
)
{
...
...
@@ -164,6 +253,7 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
BoundDecisionDagNode
node
=
nodesToLower
[
i
];
if
(
loweredNodes
.
Contains
(
node
))
{
Debug
.
Assert
(!
_dagNodeLabels
.
TryGetValue
(
node
,
out
_
));
continue
;
}
...
...
@@ -239,6 +329,7 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDagNode> sortedNodes)
private
void
GenerateTest
(
BoundExpression
test
,
BoundDecisionDagNode
whenTrue
,
BoundDecisionDagNode
whenFalse
,
BoundDecisionDagNode
nextNode
)
{
// Because we have already "optimized" away tests for a constant switch expression, the test should be nontrivial.
_factory
.
Syntax
=
test
.
Syntax
;
Debug
.
Assert
(
test
!=
null
);
if
(
nextNode
==
whenFalse
)
...
...
@@ -422,10 +513,9 @@ private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
if
(
whenClause
.
WhenExpression
!=
null
&&
whenClause
.
WhenExpression
.
ConstantValue
!=
ConstantValue
.
True
)
{
// PROTOTYPE(patterns2): there should perhaps be a sequence point (for e.g. a breakpoint) on the when clause.
// However, it is not clear that is wanted for the switch expression as that would be a breakpoint where the stack is nonempty.
_factory
.
Syntax
=
whenClause
.
Syntax
;
sectionBuilder
.
Add
(
_factory
.
ConditionalGoto
(
_localRewriter
.
VisitExpression
(
whenClause
.
WhenExpression
),
trueLabel
,
jumpIfTrue
:
true
));
Debug
.
Assert
(
whenFalse
!=
null
);
Debug
.
Assert
(
_backwardLabels
.
Contains
(
whenFalse
));
sectionBuilder
.
Add
(
_factory
.
Goto
(
GetDagNodeLabel
(
whenFalse
)));
}
else
...
...
@@ -440,6 +530,7 @@ private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
/// </summary>
private
void
LowerDecisionDagNode
(
BoundDecisionDagNode
node
,
BoundDecisionDagNode
nextNode
)
{
_factory
.
Syntax
=
node
.
Syntax
;
switch
(
node
)
{
case
BoundEvaluationDecisionDagNode
evaluationNode
:
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs
浏览文件 @
cec28aa3
...
...
@@ -4,6 +4,7 @@
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
System.Diagnostics
;
using
System.Linq
;
using
Microsoft.CodeAnalysis.CSharp.Symbols
;
using
Microsoft.CodeAnalysis.CSharp.Syntax
;
using
Microsoft.CodeAnalysis.PooledObjects
;
...
...
@@ -28,13 +29,6 @@ public static BoundStatement Rewrite(LocalRewriter localRewriter, BoundPatternSw
return
result
;
}
/// <summary>
/// Map from switch section's syntax to the lowered code for the section. The code for a section
/// includes the code to assign to the pattern variables and evaluate the when clause. Since a
/// when clause can yield a false value, it can jump back to a label in the lowered decision dag.
/// </summary>
private
Dictionary
<
SyntaxNode
,
ArrayBuilder
<
BoundStatement
>>
_switchSections
=>
base
.
_switchArms
;
/// <summary>
/// A map from section syntax to the first label in that section.
/// </summary>
...
...
@@ -69,30 +63,24 @@ protected override LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
}
private
PatternSwitchLocalRewriter
(
BoundPatternSwitchStatement
node
,
LocalRewriter
localRewriter
)
:
base
(
localRewriter
,
localRewriter
.
VisitExpression
(
node
.
Expression
),
node
.
SwitchSections
.
SelectAsArray
(
section
=>
section
.
Syntax
),
node
.
DecisionDag
)
:
base
(
node
.
Syntax
,
localRewriter
,
node
.
SwitchSections
.
SelectAsArray
(
section
=>
section
.
Syntax
),
isSwitchStatement
:
true
)
{
}
private
BoundStatement
LowerPatternSwitchStatement
(
BoundPatternSwitchStatement
node
)
{
var
reachableLabels
=
node
.
DecisionDag
.
ReachableLabels
;
_factory
.
Syntax
=
node
.
Syntax
;
var
result
=
ArrayBuilder
<
BoundStatement
>.
GetInstance
();
// The set of variables attached to the outer block
var
outerVariables
=
ArrayBuilder
<
LocalSymbol
>.
GetInstance
();
outerVariables
.
AddRange
(
node
.
InnerLocals
);
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the expression are being executed.
var
expression
=
_loweredInput
;
var
loweredSwitchGoverningExpression
=
_localRewriter
.
VisitExpression
(
node
.
Expression
);
if
(!
node
.
WasCompilerGenerated
&&
_localRewriter
.
Instrument
)
{
var
instrumentedExpression
=
_localRewriter
.
_instrumenter
.
InstrumentSwitchStatementExpression
(
node
,
expression
,
_factory
);
if
(
expression
.
ConstantValue
==
null
)
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the expression are being executed.
var
instrumentedExpression
=
_localRewriter
.
_instrumenter
.
InstrumentSwitchStatementExpression
(
node
,
loweredSwitchGoverningExpression
,
_factory
);
if
(
loweredSwitchGoverningExpression
.
ConstantValue
==
null
)
{
e
xpression
=
instrumentedExpression
;
loweredSwitchGoverningE
xpression
=
instrumentedExpression
;
}
else
{
...
...
@@ -102,20 +90,18 @@ private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement n
}
}
// Assign the input to a temp
BoundExpression
inputTemp
=
_tempAllocator
.
GetTemp
(
base
.
_inputTemp
);
if
(
inputTemp
==
expression
)
{
// In this case we would just be assigning the variable to itself, so we need generate no code.
// This arises due to an optimization by which we use pattern variables as pattern-matching temps.
}
else
{
result
.
Add
(
_factory
.
Assignment
(
inputTemp
,
expression
));
}
// The set of variables attached to the outer block
outerVariables
.
AddRange
(
node
.
InnerLocals
);
// Evaluate the input and set up sharing for dag temps with user variables
BoundDecisionDag
decisionDag
=
ShareTempsIfPossibleAndEvaluateInput
(
node
.
DecisionDag
,
loweredSwitchGoverningExpression
,
result
);
// lower the decision dag.
(
ImmutableArray
<
BoundStatement
>
loweredDag
,
ImmutableDictionary
<
SyntaxNode
,
ImmutableArray
<
BoundStatement
>>
switchSections
)
=
LowerDecisionDag
(
decisionDag
);
// then add the rest of the lowered dag that references that input
result
.
Add
(
_factory
.
Block
(
this
.
_loweredDecisionDag
.
ToImmutable
()
));
result
.
Add
(
_factory
.
Block
(
loweredDag
));
// A branch to the default label when no switch case matches is included in the
// decision dag, so the code in `result` is unreachable at this point.
...
...
@@ -124,7 +110,8 @@ private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement n
foreach
(
BoundPatternSwitchSection
section
in
node
.
SwitchSections
)
{
_factory
.
Syntax
=
section
.
Syntax
;
ArrayBuilder
<
BoundStatement
>
sectionBuilder
=
_switchSections
[
section
.
Syntax
];
var
sectionBuilder
=
ArrayBuilder
<
BoundStatement
>.
GetInstance
();
sectionBuilder
.
AddRange
(
switchSections
[
section
.
Syntax
]);
foreach
(
BoundPatternSwitchLabel
switchLabel
in
section
.
SwitchLabels
)
{
sectionBuilder
.
Add
(
_factory
.
Label
(
switchLabel
.
Label
));
...
...
@@ -147,8 +134,8 @@ private BoundStatement LowerPatternSwitchStatement(BoundPatternSwitchStatement n
}
else
{
// Note the scope of the locals, even though they are included for the purposes of
//
closur
e analysis in the enclosing scope.
// Note the
language
scope of the locals, even though they are included for the purposes of
//
lifetim
e analysis in the enclosing scope.
result
.
Add
(
new
BoundScope
(
section
.
Syntax
,
section
.
Locals
,
statements
));
}
}
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs
浏览文件 @
cec28aa3
...
...
@@ -4,6 +4,7 @@
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
System.Diagnostics
;
using
System.Linq
;
using
Microsoft.CodeAnalysis.CSharp.Symbols
;
using
Microsoft.CodeAnalysis.PooledObjects
;
using
Roslyn.Utilities
;
...
...
@@ -18,19 +19,14 @@ internal sealed partial class LocalRewriter
private
class
PatternLocalRewriter
{
protected
readonly
LocalRewriter
_localRewriter
;
protected
readonly
BoundExpression
_loweredInput
;
protected
readonly
SyntheticBoundNodeFactory
_factory
;
protected
readonly
DagTempAllocator
_tempAllocator
;
protected
readonly
BoundDagTemp
_inputTemp
;
public
PatternLocalRewriter
(
LocalRewriter
localRewriter
,
BoundExpression
loweredInput
)
public
PatternLocalRewriter
(
SyntaxNode
node
,
LocalRewriter
localRewriter
)
{
this
.
_loweredInput
=
loweredInput
;
this
.
_localRewriter
=
localRewriter
;
this
.
_factory
=
localRewriter
.
_factory
;
this
.
_factory
.
Syntax
=
_loweredInput
.
Syntax
;
this
.
_tempAllocator
=
new
DagTempAllocator
(
_factory
);
this
.
_inputTemp
=
new
BoundDagTemp
(
loweredInput
.
Syntax
,
loweredInput
.
Type
,
source
:
null
,
index
:
0
);
this
.
_tempAllocator
=
new
DagTempAllocator
(
_factory
,
node
);
}
public
void
Free
()
...
...
@@ -38,15 +34,19 @@ public void Free()
_tempAllocator
.
Free
();
}
protected
BoundDagTemp
InputTemp
(
BoundExpression
expr
)
=>
new
BoundDagTemp
(
expr
.
Syntax
,
expr
.
Type
,
null
,
0
);
public
class
DagTempAllocator
{
private
readonly
SyntheticBoundNodeFactory
_factory
;
private
readonly
PooledDictionary
<
BoundDagTemp
,
BoundExpression
>
_map
=
PooledDictionary
<
BoundDagTemp
,
BoundExpression
>.
GetInstance
();
private
readonly
ArrayBuilder
<
LocalSymbol
>
_temps
=
ArrayBuilder
<
LocalSymbol
>.
GetInstance
();
private
readonly
SyntaxNode
_node
;
public
DagTempAllocator
(
SyntheticBoundNodeFactory
factory
)
public
DagTempAllocator
(
SyntheticBoundNodeFactory
factory
,
SyntaxNode
node
)
{
this
.
_factory
=
factory
;
this
.
_node
=
node
;
}
public
void
Free
()
...
...
@@ -59,8 +59,7 @@ public BoundExpression GetTemp(BoundDagTemp dagTemp)
{
if
(!
_map
.
TryGetValue
(
dagTemp
,
out
BoundExpression
result
))
{
// PROTOTYPE(patterns2): Not sure what temp kind should be used for `is pattern`.
LocalSymbol
temp
=
_factory
.
SynthesizedLocal
(
dagTemp
.
Type
,
syntax
:
_factory
.
Syntax
,
kind
:
SynthesizedLocalKind
.
SwitchCasePatternMatching
);
LocalSymbol
temp
=
_factory
.
SynthesizedLocal
(
dagTemp
.
Type
,
syntax
:
_node
,
kind
:
SynthesizedLocalKind
.
SwitchCasePatternMatching
);
result
=
_factory
.
Local
(
temp
);
_map
.
Add
(
dagTemp
,
result
);
_temps
.
Add
(
temp
);
...
...
@@ -204,12 +203,11 @@ void addArg(RefKind refKind, BoundExpression expression)
/// </summary>
protected
BoundExpression
LowerTest
(
BoundDagTest
test
)
{
_factory
.
Syntax
=
test
.
Syntax
;
BoundExpression
input
=
_tempAllocator
.
GetTemp
(
test
.
Input
);
switch
(
test
)
{
case
BoundDagNonNullTest
d
:
// If the actual input is a constant, the test should have been removed from the decision dag
Debug
.
Assert
(!(
d
.
Input
==
_inputTemp
&&
_loweredInput
.
ConstantValue
!=
null
));
return
_localRewriter
.
MakeNullCheck
(
d
.
Syntax
,
input
,
input
.
Type
.
IsNullableType
()
?
BinaryOperatorKind
.
NullableNullNotEqual
:
BinaryOperatorKind
.
NotEqual
);
case
BoundDagTypeTest
d
:
...
...
@@ -217,13 +215,9 @@ protected BoundExpression LowerTest(BoundDagTest test)
return
_factory
.
Is
(
input
,
d
.
Type
);
case
BoundDagNullTest
d
:
// If the actual input is a constant, the test should have been removed from the decision dag
Debug
.
Assert
(!(
d
.
Input
==
_inputTemp
&&
_loweredInput
.
ConstantValue
!=
null
));
return
_localRewriter
.
MakeNullCheck
(
d
.
Syntax
,
input
,
input
.
Type
.
IsNullableType
()
?
BinaryOperatorKind
.
NullableNullEqual
:
BinaryOperatorKind
.
Equal
);
case
BoundDagValueTest
d
:
// If the actual input is a constant, the test should have been removed from the decision dag
Debug
.
Assert
(!(
d
.
Input
==
_inputTemp
&&
_loweredInput
.
ConstantValue
!=
null
));
Debug
.
Assert
(!
input
.
Type
.
IsNullableType
());
return
_localRewriter
.
MakeEqual
(
_localRewriter
.
MakeLiteral
(
d
.
Syntax
,
d
.
Value
,
input
.
Type
),
input
);
...
...
@@ -268,26 +262,33 @@ protected BoundExpression LowerTest(BoundDagTest test)
return
false
;
}
protected
void
ShareTemps
(
ImmutableArray
<
BoundDecisionDagNode
>
sortedNodes
)
/// <summary>
/// Produce assignment of the input expression. This method is also responsible for assigning
/// variables for some pattern-matching temps that can be shared with user variables.
/// </summary>
protected
BoundDecisionDag
ShareTempsAndEvaluateInput
(
BoundExpression
loweredInput
,
BoundDecisionDag
decisionDag
,
Action
<
BoundExpression
>
addCode
)
{
if
(
_loweredInput
.
Kind
==
BoundKind
.
Local
||
_loweredInput
.
Kind
==
BoundKind
.
Parameter
)
var
inputDagTemp
=
InputTemp
(
loweredInput
);
if
(
loweredInput
.
Kind
==
BoundKind
.
Local
||
loweredInput
.
Kind
==
BoundKind
.
Parameter
)
{
// If we're switching on a local variable and there is no when clause (checked by the caller),
// we assume the value of the local variable does not change during the execution of the
// decision automaton and we just reuse the local variable when we need the input expression.
// It is possible for this assumption to be violated by a side-effecting Deconstruct that
// modifies the local variable which has been captured in a lambda. Since the language assumes
// that functions called
during
pattern-matching are idempotent and not side-effecting, we feel
// that functions called
by
pattern-matching are idempotent and not side-effecting, we feel
// justified in taking this assumption in the compiler too.
_
=
_tempAllocator
.
TrySetTemp
(
_inputTemp
,
_loweredInput
);
bool
tempAssigned
=
_tempAllocator
.
TrySetTemp
(
inputDagTemp
,
loweredInput
);
Debug
.
Assert
(
tempAssigned
);
}
foreach
(
BoundDecisionDagNode
node
in
s
ortedNodes
)
foreach
(
BoundDecisionDagNode
node
in
decisionDag
.
TopologicallyS
ortedNodes
)
{
if
(
node
is
BoundWhenDecisionDagNode
w
)
{
Debug
.
Assert
(
w
.
WhenExpression
==
null
||
w
.
WhenExpression
.
ConstantValue
==
ConstantValue
.
True
);
// We share a slot for a user-declared pattern-matching variable with a pattern temp if there
// is no user-written when-clause that could modify the variable before the matching
// automaton is done with it (checked by the caller).
...
...
@@ -301,6 +302,65 @@ protected void ShareTemps(ImmutableArray<BoundDecisionDagNode> sortedNodes)
}
}
}
if
(
loweredInput
.
Kind
==
BoundKind
.
TupleLiteral
&&
!
decisionDag
.
TopologicallySortedNodes
.
Any
(
n
=>
!
usesOriginalInput
(
n
))
&&
false
)
{
// If the switch governing expression is a tuple literal that is not used anywhere,
// (though perhaps its component parts are used), then we can save the component parts
// and assign them into temps (or perhaps user variables) to avoid the creation of
// the tuple altogether.
decisionDag
=
RewriteTupleSwitch
(
decisionDag
,
(
BoundTupleLiteral
)
loweredInput
,
addCode
);
}
else
{
// Otherwise we emit an assignment of the input expression to a temporary variable.
BoundExpression
inputTemp
=
_tempAllocator
.
GetTemp
(
inputDagTemp
);
if
(
inputTemp
!=
loweredInput
)
{
addCode
(
_factory
.
AssignmentExpression
(
inputTemp
,
loweredInput
));
}
}
return
decisionDag
;
bool
usesOriginalInput
(
BoundDecisionDagNode
node
)
{
switch
(
node
)
{
case
BoundWhenDecisionDagNode
n
:
return
(
n
.
Bindings
.
Any
(
b
=>
b
.
dagTemp
.
IsOriginalInput
));
case
BoundTestDecisionDagNode
t
:
return
t
.
Test
.
Input
.
IsOriginalInput
;
case
BoundEvaluationDecisionDagNode
e
:
switch
(
e
.
Evaluation
)
{
case
BoundDagFieldEvaluation
f
:
// Uses only a tuple element, not the whole input
return
f
.
Input
.
IsOriginalInput
&&
!
f
.
Field
.
IsTupleElement
();
default
:
return
e
.
Evaluation
.
Input
.
IsOriginalInput
;
}
default
:
return
false
;
}
}
}
/// <summary>
/// We have a decision dag whose input is a tuple literal, and the decision dag does not need the tuple itself.
/// We rewrite the decision dag into one which doesn't touch the tuple, but instead works directly with the
/// values that have been stored in temps. This permits the caller to avoid creation of the tuple object
/// itself. We also emit assignments of the tuple values into their corresponding temps.
/// </summary>
/// <returns>A new decision dag that does not reference the input directly</returns>
private
BoundDecisionDag
RewriteTupleSwitch
(
BoundDecisionDag
decisionDag
,
BoundTupleLiteral
loweredInput
,
Action
<
BoundExpression
>
addCode
)
{
throw
new
NotImplementedException
();
}
}
...
...
@@ -318,8 +378,8 @@ private class IsPatternExpressionLocalRewriter : PatternLocalRewriter
/// </summary>
private
readonly
ArrayBuilder
<
BoundExpression
>
_conjunctBuilder
;
public
IsPatternExpressionLocalRewriter
(
LocalRewriter
localRewriter
,
BoundExpression
loweredInput
)
:
base
(
localRewriter
,
loweredInput
)
public
IsPatternExpressionLocalRewriter
(
SyntaxNode
node
,
LocalRewriter
localRewriter
)
:
base
(
node
,
localRewriter
)
{
this
.
_conjunctBuilder
=
ArrayBuilder
<
BoundExpression
>.
GetInstance
();
this
.
_sideEffectBuilder
=
ArrayBuilder
<
BoundExpression
>.
GetInstance
();
...
...
@@ -370,25 +430,20 @@ private void LowerOneTest(BoundDagTest test)
}
}
public
BoundExpression
LowerIsPattern
(
BoundPattern
pattern
,
CSharpCompilation
compilation
,
DiagnosticBag
diagnostics
)
public
BoundExpression
LowerIsPattern
(
Bound
Expression
loweredInput
,
Bound
Pattern
pattern
,
CSharpCompilation
compilation
,
DiagnosticBag
diagnostics
)
{
LabelSymbol
failureLabel
=
new
GeneratedLabelSymbol
(
"failure"
);
BoundDecisionDag
d
ag
=
DecisionDagBuilder
.
CreateDecisionDagForIsPattern
(
compilation
,
pattern
.
Syntax
,
this
.
_
loweredInput
,
pattern
,
failureLabel
,
diagnostics
,
out
LabelSymbol
successLabel
);
if
(
_
loweredInput
.
ConstantValue
!=
null
)
BoundDecisionDag
d
ecisionDag
=
DecisionDagBuilder
.
CreateDecisionDagForIsPattern
(
compilation
,
pattern
.
Syntax
,
loweredInput
,
pattern
,
failureLabel
,
diagnostics
,
out
LabelSymbol
successLabel
);
if
(
loweredInput
.
ConstantValue
!=
null
)
{
d
ag
=
dag
.
SimplifyDecisionDagForConstantInput
(
_
loweredInput
,
_localRewriter
.
_compilation
.
Conversions
,
diagnostics
);
d
ecisionDag
=
decisionDag
.
SimplifyDecisionDagForConstantInput
(
loweredInput
,
_localRewriter
.
_compilation
.
Conversions
,
diagnostics
);
}
// The optimization of sharing pattern-matching temps with user variables can always apply to
// an is-pattern expression because there is no when clause.
ShareTemps
(
dag
.
TopologicallySortedNodes
);
var
inputTemp
=
_tempAllocator
.
GetTemp
(
_inputTemp
);
if
(
inputTemp
!=
_loweredInput
)
{
_sideEffectBuilder
.
Add
(
_factory
.
AssignmentExpression
(
inputTemp
,
_loweredInput
));
}
var
node
=
dag
.
RootNode
;
// an is-pattern expression because there is no when clause that could possibly intervene during
// the execution of the pattern-matching automaton and change one of those variables.
decisionDag
=
ShareTempsAndEvaluateInput
(
loweredInput
,
decisionDag
,
expr
=>
_sideEffectBuilder
.
Add
(
expr
));
var
node
=
decisionDag
.
RootNode
;
// We follow the "good" path in the decision dag. We depend on it being nicely linear in structure.
// If we add "or" patterns that assumption breaks down.
...
...
@@ -457,7 +512,7 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
break
;
default
:
throw
ExceptionUtilities
.
UnexpectedValue
(
dag
.
Kind
);
throw
ExceptionUtilities
.
UnexpectedValue
(
d
ecisionD
ag
.
Kind
);
}
if
(
_sideEffectBuilder
.
Count
>
0
||
_conjunctBuilder
.
Count
==
0
)
...
...
@@ -551,8 +606,8 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node
{
BoundExpression
loweredExpression
=
VisitExpression
(
node
.
Expression
);
BoundPattern
loweredPattern
=
LowerPattern
(
node
.
Pattern
);
var
isPatternRewriter
=
new
IsPatternExpressionLocalRewriter
(
this
,
loweredExpression
);
BoundExpression
result
=
isPatternRewriter
.
LowerIsPattern
(
loweredPattern
,
this
.
_compilation
,
this
.
_diagnostics
);
var
isPatternRewriter
=
new
IsPatternExpressionLocalRewriter
(
node
.
Syntax
,
this
);
BoundExpression
result
=
isPatternRewriter
.
LowerIsPattern
(
lowered
Expression
,
lowered
Pattern
,
this
.
_compilation
,
this
.
_diagnostics
);
isPatternRewriter
.
Free
();
return
result
;
}
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
浏览文件 @
cec28aa3
...
...
@@ -2,6 +2,9 @@
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
System.Diagnostics
;
using
System.Linq
;
using
Microsoft.CodeAnalysis.CSharp.Symbols
;
using
Microsoft.CodeAnalysis.PooledObjects
;
...
...
@@ -21,14 +24,14 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
private
class
SwitchExpressionLocalRewriter
:
BasePatternSwitchLocalRewriter
{
private
SwitchExpressionLocalRewriter
(
LocalRewriter
localRewriter
,
BoundSwitchExpression
node
)
:
base
(
localRewriter
,
localRewriter
.
VisitExpression
(
node
.
Expression
),
node
.
SwitchArms
.
SelectAsArray
(
arm
=>
arm
.
Syntax
),
node
.
DecisionDag
)
private
SwitchExpressionLocalRewriter
(
BoundSwitchExpression
node
,
LocalRewriter
localRewriter
)
:
base
(
node
.
Syntax
,
localRewriter
,
node
.
SwitchArms
.
SelectAsArray
(
arm
=>
arm
.
Syntax
),
isSwitchStatement
:
false
)
{
}
public
static
BoundExpression
Rewrite
(
LocalRewriter
localRewriter
,
BoundSwitchExpression
node
)
{
var
rewriter
=
new
SwitchExpressionLocalRewriter
(
localRewriter
,
node
);
var
rewriter
=
new
SwitchExpressionLocalRewriter
(
node
,
localRewriter
);
BoundExpression
result
=
rewriter
.
LowerSwitchExpression
(
node
);
rewriter
.
Free
();
return
result
;
...
...
@@ -36,41 +39,41 @@ public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundSwitchEx
private
BoundExpression
LowerSwitchExpression
(
BoundSwitchExpression
node
)
{
_factory
.
Syntax
=
node
.
Syntax
;
var
result
=
ArrayBuilder
<
BoundStatement
>.
GetInstance
();
var
locals
=
ArrayBuilder
<
LocalSymbol
>.
GetInstance
();
LocalSymbol
resultTemp
=
_factory
.
SynthesizedLocal
(
node
.
Type
,
node
.
Syntax
,
kind
:
SynthesizedLocalKind
.
SwitchCasePatternMatching
);
LabelSymbol
afterSwitchExpression
=
_factory
.
GenerateLabel
(
"afterSwitchExpression"
);
var
loweredSwitchGoverningExpression
=
_localRewriter
.
VisitExpression
(
node
.
Expression
);
// Note that a when-clause can contain an assignment to a
// pattern variable declared in a different when-clause (e.g. in the same section, or
// in a different section via the use of a local function), so we need to analyze all
// of the when clauses to see if they are all simple enough to conclude that they do
// not mutate pattern variables.
BoundDecisionDag
decisionDag
=
ShareTempsIfPossibleAndEvaluateInput
(
node
.
DecisionDag
,
loweredSwitchGoverningExpression
,
result
);
// Assign the input to a temp
result
.
Add
(
_factory
.
Assignment
(
_tempAllocator
.
GetTemp
(
base
.
_inputTemp
),
base
.
_loweredInput
));
// lower the decision dag.
(
ImmutableArray
<
BoundStatement
>
loweredDag
,
ImmutableDictionary
<
SyntaxNode
,
ImmutableArray
<
BoundStatement
>>
switchSections
)
=
LowerDecisionDag
(
decisionDag
);
// then
lower the rest of the
dag that references that input
result
.
Add
Range
(
_loweredDecisionDag
.
ToImmutable
(
));
// then
add the rest of the lowered
dag that references that input
result
.
Add
(
_factory
.
Block
(
loweredDag
));
// A branch to the default label when no switch case matches is included in the
// decision tree, so the code in result is unreachable at this point.
// Lower each switch arm
// Lower each switch expression arm
LocalSymbol
resultTemp
=
_factory
.
SynthesizedLocal
(
node
.
Type
,
node
.
Syntax
,
kind
:
SynthesizedLocalKind
.
SwitchCasePatternMatching
);
LabelSymbol
afterSwitchExpression
=
_factory
.
GenerateLabel
(
"afterSwitchExpression"
);
foreach
(
BoundSwitchExpressionArm
arm
in
node
.
SwitchArms
)
{
ArrayBuilder
<
BoundStatement
>
sectionStatementBuilder
=
_switchArms
[
arm
.
Syntax
];
result
.
Add
(
_factory
.
Label
(
arm
.
Label
));
sectionStatementBuilder
.
Add
(
_factory
.
Assignment
(
_factory
.
Local
(
resultTemp
),
_localRewriter
.
VisitExpression
(
arm
.
Value
)));
var
statements
=
sectionStatementBuilder
.
ToImmutableAndFree
();
if
(
arm
.
Locals
.
IsEmpty
)
{
result
.
Add
(
_factory
.
StatementList
(
statements
));
}
else
{
// even though the whole switch expression will be lifted to the statement level, we
// want the locals of each section to see a section-specific scope.
result
.
Add
(
new
BoundScope
(
arm
.
Syntax
,
arm
.
Locals
,
statements
));
locals
.
AddRange
(
arm
.
Locals
);
}
result
.
Add
(
_factory
.
Goto
(
afterSwitchExpression
));
_factory
.
Syntax
=
arm
.
Syntax
;
var
sectionBuilder
=
ArrayBuilder
<
BoundStatement
>.
GetInstance
();
sectionBuilder
.
AddRange
(
switchSections
[
arm
.
Syntax
]);
sectionBuilder
.
Add
(
_factory
.
Label
(
arm
.
Label
));
sectionBuilder
.
Add
(
_factory
.
Assignment
(
_factory
.
Local
(
resultTemp
),
_localRewriter
.
VisitExpression
(
arm
.
Value
)));
sectionBuilder
.
Add
(
_factory
.
Goto
(
afterSwitchExpression
));
result
.
Add
(
new
BoundBlock
(
arm
.
Syntax
,
arm
.
Locals
,
sectionBuilder
.
ToImmutableAndFree
()));
}
_factory
.
Syntax
=
node
.
Syntax
;
if
(
node
.
DefaultLabel
!=
null
)
{
result
.
Add
(
_factory
.
Label
(
node
.
DefaultLabel
));
...
...
@@ -79,9 +82,7 @@ private BoundExpression LowerSwitchExpression(BoundSwitchExpression node)
}
result
.
Add
(
_factory
.
Label
(
afterSwitchExpression
));
locals
.
AddRange
(
_tempAllocator
.
AllTemps
());
locals
.
Add
(
resultTemp
);
return
_factory
.
SpillSequence
(
locals
.
ToImmutableAndFree
(),
result
.
ToImmutableAndFree
(),
_factory
.
Local
(
resultTemp
));
return
_factory
.
SpillSequence
(
_tempAllocator
.
AllTemps
().
Add
(
resultTemp
),
result
.
ToImmutableAndFree
(),
_factory
.
Local
(
resultTemp
));
}
}
}
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
浏览文件 @
cec28aa3
...
...
@@ -91,9 +91,9 @@ static void M1(int? x)
// (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault'
// case int i: break;
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"int i"
).
WithArguments
(
"System.Nullable`1"
,
"GetValueOrDefault"
).
WithLocation
(
14
,
18
),
// (1
2,17
): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value'
//
switch (x)
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"
x"
).
WithArguments
(
"System.Nullable`1"
,
"get_Value"
).
WithLocation
(
12
,
17
),
// (1
4,18
): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value'
//
case int i: break;
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"
int i"
).
WithArguments
(
"System.Nullable`1"
,
"get_Value"
).
WithLocation
(
14
,
18
),
// (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue'
// static bool M2(int? x) => x is int i;
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"int i"
).
WithArguments
(
"System.Nullable`1"
,
"get_HasValue"
).
WithLocation
(
17
,
36
),
...
...
@@ -339,12 +339,13 @@ static void Main(string[] args)
var
compVerifier
=
CompileAndVerify
(
compilation
,
expectedOutput
:
expectedOutput
);
compVerifier
.
VerifyIL
(
"Program.Main"
,
@"{
// Code size 8
2 (0x52
)
// Code size 8
4 (0x54
)
.maxstack 2
.locals init (Base<object> V_0, //x
Derived V_1, //y
Derived V_2, //z
Base<object> V_3)
Base<object> V_3,
Base<object> V_4)
IL_0000: nop
IL_0001: newobj ""Derived..ctor()""
IL_0006: stloc.0
...
...
@@ -363,27 +364,27 @@ .maxstack 2
IL_0021: call ""void System.Console.WriteLine(bool)""
IL_0026: nop
IL_0027: ldloc.0
IL_0028: stloc.
3
IL_002
9: ldloc.3
IL_002
a: stloc.0
IL_002
b: ldloc.0
IL_002
c
: isinst ""Derived""
IL_003
1
: stloc.2
IL_003
2
: ldloc.2
IL_003
3: brtrue.s IL_0037
IL_003
5: br.s IL_0042
IL_003
7: br.s IL_0039
IL_003
9
: ldc.i4.1
IL_003
a
: call ""void System.Console.WriteLine(bool)""
IL_00
3f
: nop
IL_004
0: br.s IL_0042
IL_004
2
: ldloc.0
IL_004
3
: isinst ""Derived""
IL_004
8
: ldnull
IL_004
9
: cgt.un
IL_004
b
: call ""void System.Console.WriteLine(bool)""
IL_005
0
: nop
IL_005
1
: ret
IL_0028: stloc.
s V_4
IL_002
a: ldloc.s V_4
IL_002
c: stloc.3
IL_002
d: ldloc.3
IL_002
e
: isinst ""Derived""
IL_003
3
: stloc.2
IL_003
4
: ldloc.2
IL_003
5: brtrue.s IL_0039
IL_003
7: br.s IL_0044
IL_003
9: br.s IL_003b
IL_003
b
: ldc.i4.1
IL_003
c
: call ""void System.Console.WriteLine(bool)""
IL_00
41
: nop
IL_004
2: br.s IL_0044
IL_004
4
: ldloc.0
IL_004
5
: isinst ""Derived""
IL_004
a
: ldnull
IL_004
b
: cgt.un
IL_004
d
: call ""void System.Console.WriteLine(bool)""
IL_005
2
: nop
IL_005
3
: ret
}"
);
}
...
...
@@ -469,60 +470,61 @@ .maxstack 2
}"
);
compVerifier
.
VerifyIL
(
"Program.P5"
,
@"{
// Code size
98 (0x62
)
// Code size
103 (0x67
)
.maxstack 2
.locals init (double V_0,
float V_1,
object V_2,
bool V_3)
.locals init (object V_0,
double V_1,
float V_2,
object V_3,
bool V_4)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.
2
IL_0003: ldloc.
2
IL_0004: st
arg.s V_
0
IL_000
6: ldarg
.0
IL_000
7
: isinst ""double""
IL_000
c: brfalse.s IL_002b
IL_000
e: ldarg
.0
IL_000
f
: unbox.any ""double""
IL_001
4: stloc.0
IL_001
5: ldloc.0
IL_001
6
: call ""bool double.IsNaN(double)""
IL_001
b: brtrue.s IL_004c
IL_001
d
: ldc.r8 3.14
IL_002
6: ldloc.0
IL_002
7: beq.s IL_0054
IL_002
9: br.s IL_005c
IL_002
b: ldarg
.0
IL_002
c
: isinst ""float""
IL_003
1: brfalse.s IL_005c
IL_003
3: ldarg
.0
IL_003
4
: unbox.any ""float""
IL_003
9: stloc.1
IL_003
a: ldloc.1
IL_003
b
: call ""bool float.IsNaN(float)""
IL_00
40
: brtrue.s IL_0050
IL_004
2
: ldc.r4 3.14
IL_004
7: ldloc.1
IL_004
8: beq.s IL_0058
IL_004
a: br.s IL_005c
IL_004
c
: ldc.i4.1
IL_004
d: stloc.3
IL_004e: br.s IL_006
0
IL_0002: stloc.
3
IL_0003: ldloc.
3
IL_0004: st
loc.
0
IL_000
5: ldloc
.0
IL_000
6
: isinst ""double""
IL_000
b: brfalse.s IL_002a
IL_000
d: ldloc
.0
IL_000
e
: unbox.any ""double""
IL_001
3: stloc.1
IL_001
4: ldloc.1
IL_001
5
: call ""bool double.IsNaN(double)""
IL_001
a: brtrue.s IL_004b
IL_001
c
: ldc.r8 3.14
IL_002
5: ldloc.1
IL_002
6: beq.s IL_0055
IL_002
8: br.s IL_005f
IL_002
a: ldloc
.0
IL_002
b
: isinst ""float""
IL_003
0: brfalse.s IL_005f
IL_003
2: ldloc
.0
IL_003
3
: unbox.any ""float""
IL_003
8: stloc.2
IL_003
9: ldloc.2
IL_003
a
: call ""bool float.IsNaN(float)""
IL_00
3f
: brtrue.s IL_0050
IL_004
1
: ldc.r4 3.14
IL_004
6: ldloc.2
IL_004
7: beq.s IL_005a
IL_004
9: br.s IL_005f
IL_004
b
: ldc.i4.1
IL_004
c: stloc.s V_4
IL_004e: br.s IL_006
4
IL_0050: ldc.i4.1
IL_0051: stloc.
3
IL_005
2: br.s IL_0060
IL_005
4
: ldc.i4.1
IL_005
5: stloc.3
IL_005
6: br.s IL_0060
IL_005
8
: ldc.i4.1
IL_005
9: stloc.3
IL_005
a: br.s IL_0060
IL_005
c
: ldc.i4.0
IL_00
5d: stloc.3
IL_00
5e: br.s IL_0060
IL_006
0: ldloc.3
IL_006
1
: ret
IL_0051: stloc.
s V_4
IL_005
3: br.s IL_0064
IL_005
5
: ldc.i4.1
IL_005
6: stloc.s V_4
IL_005
8: br.s IL_0064
IL_005
a
: ldc.i4.1
IL_005
b: stloc.s V_4
IL_005
d: br.s IL_0064
IL_005
f
: ldc.i4.0
IL_00
60: stloc.s V_4
IL_00
62: br.s IL_0064
IL_006
4: ldloc.s V_4
IL_006
6
: ret
}"
);
}
...
...
@@ -708,35 +710,32 @@ static int M2(object o, bool b)
var
compVerifier
=
CompileAndVerify
(
compilation
);
compVerifier
.
VerifyIL
(
"C.M1"
,
@"{
// Code size 3
5 (0x23
)
// Code size 3
3 (0x21
)
.maxstack 1
.locals init (bool? V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloca.s V_0
IL_0004: call ""bool bool?.HasValue.get""
IL_0009: brfalse.s IL_0018
IL_000b: br.s IL_001a
IL_000d: ldloca.s V_0
IL_000f: call ""bool bool?.GetValueOrDefault()""
IL_0014: brtrue.s IL_001f
IL_0016: br.s IL_0021
IL_0018: ldc.i4.1
IL_0019: ret
IL_001a: ldarg.1
IL_001b: brfalse.s IL_000d
IL_001d: ldc.i4.2
IL_0000: ldarga.s V_0
IL_0002: call ""bool bool?.HasValue.get""
IL_0007: brfalse.s IL_0016
IL_0009: br.s IL_0018
IL_000b: ldarga.s V_0
IL_000d: call ""bool bool?.GetValueOrDefault()""
IL_0012: brtrue.s IL_001d
IL_0014: br.s IL_001f
IL_0016: ldc.i4.1
IL_0017: ret
IL_0018: ldarg.1
IL_0019: brfalse.s IL_000b
IL_001b: ldc.i4.2
IL_001c: ret
IL_001d: ldc.i4.3
IL_001e: ret
IL_001f: ldc.i4.
3
IL_001f: ldc.i4.
4
IL_0020: ret
IL_0021: ldc.i4.4
IL_0022: ret
}"
);
compVerifier
.
VerifyIL
(
"C.M2"
,
@"{
// Code size 19 (0x13)
.maxstack 1
.locals init (string V_0)
.locals init (string V_0)
//a
IL_0000: ldarg.0
IL_0001: isinst ""string""
IL_0006: stloc.0
...
...
@@ -803,41 +802,35 @@ public void SwitchBasedPatternMatching(object o)
var
compVerifier
=
CompileAndVerify
(
compilation
);
compVerifier
.
VerifyIL
(
"C.SwitchBasedPatternMatching"
,
@"{
// Code size
71 (0x47
)
// Code size
67 (0x43
)
.maxstack 2
.locals init (int V_0,
string V_1,
object V_2)
.locals init (int V_0) //n
IL_0000: ldarg.1
IL_0001: stloc.2
IL_0002: ldloc.2
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0013
IL_000a: ldloc.2
IL_000b: unbox.any ""int""
IL_0010: stloc.0
IL_0011: br.s IL_001e
IL_0013: ldloc.2
IL_0014: isinst ""string""
IL_0019: stloc.1
IL_001a: ldloc.1
IL_001b: brtrue.s IL_002d
IL_001d: ret
IL_001e: ldloc.0
IL_001f: ldc.i4.1
IL_0020: bne.un.s IL_0038
IL_0022: ldstr ""1""
IL_0027: call ""void System.Console.WriteLine(string)""
IL_002c: ret
IL_002d: ldstr ""s""
IL_0032: call ""void System.Console.WriteLine(string)""
IL_0037: ret
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: bne.un.s IL_0046
IL_003c: ldstr ""2""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: ret
IL_0001: isinst ""int""
IL_0006: brfalse.s IL_0011
IL_0008: ldarg.1
IL_0009: unbox.any ""int""
IL_000e: stloc.0
IL_000f: br.s IL_001a
IL_0011: ldarg.1
IL_0012: isinst ""string""
IL_0017: brtrue.s IL_0029
IL_0019: ret
IL_001a: ldloc.0
IL_001b: ldc.i4.1
IL_001c: bne.un.s IL_0034
IL_001e: ldstr ""1""
IL_0023: call ""void System.Console.WriteLine(string)""
IL_0028: ret
IL_0029: ldstr ""s""
IL_002e: call ""void System.Console.WriteLine(string)""
IL_0033: ret
IL_0034: ldloc.0
IL_0035: ldc.i4.2
IL_0036: bne.un.s IL_0042
IL_0038: ldstr ""2""
IL_003d: call ""void System.Console.WriteLine(string)""
IL_0042: ret
}"
);
}
...
...
@@ -1204,7 +1197,7 @@ static void Main(string[] args)
@"{
// Code size 94 (0x5e)
.maxstack 3
.locals init (Door.DoorState V_0,
.locals init (Door.DoorState V_0,
//oldState
System.ValueTuple<Door.DoorState, Door.Action> V_1,
Door.Action V_2)
IL_0000: ldloca.s V_1
...
...
@@ -1260,7 +1253,7 @@ .maxstack 3
}"
);
compVerifier
.
VerifyIL
(
"Door.ChangeState1"
,
@"{
// Code size
98 (0x62
)
// Code size
104 (0x68
)
.maxstack 3
.locals init (System.ValueTuple<Door.DoorState, Door.Action> V_0,
Door.DoorState V_1,
...
...
@@ -1278,14 +1271,14 @@ .maxstack 3
IL_0024,
IL_0031,
IL_0041)
IL_0022: br.s IL_00
5e
IL_0022: br.s IL_00
64
IL_0024: ldloc.0
IL_0025: ldfld ""Door.Action System.ValueTuple<Door.DoorState, Door.Action>.Item2""
IL_002a: stloc.2
IL_002b: ldc.i4.1
IL_002c: ldloc.2
IL_002d: beq.s IL_004e
IL_002f: br.s IL_00
5e
IL_002f: br.s IL_00
64
IL_0031: ldloc.0
IL_0032: ldfld ""Door.Action System.ValueTuple<Door.DoorState, Door.Action>.Item2""
IL_0037: stloc.2
...
...
@@ -1294,29 +1287,34 @@ .maxstack 3
IL_003b: ldloc.2
IL_003c: ldc.i4.2
IL_003d: beq.s IL_0056
IL_003f: br.s IL_00
5e
IL_003f: br.s IL_00
64
IL_0041: ldloc.0
IL_0042: ldfld ""Door.Action System.ValueTuple<Door.DoorState, Door.Action>.Item2""
IL_0047: stloc.2
IL_0048: ldc.i4.3
IL_0049: ldloc.2
IL_004a: beq.s IL_005
b
IL_004c: br.s IL_00
5e
IL_004a: beq.s IL_005
d
IL_004c: br.s IL_00
64
IL_004e: ldc.i4.1
IL_004f: stloc.3
IL_0050: br.s IL_006
0
IL_0050: br.s IL_006
6
IL_0052: ldc.i4.0
IL_0053: stloc.3
IL_0054: br.s IL_006
0
IL_0054: br.s IL_006
6
IL_0056: ldarg.2
IL_0057: brtrue.s IL_0056
IL_0059: br.s IL_005e
IL_005b: ldarg.2
IL_005c: brtrue.s IL_005b
IL_005e: ldarg.0
IL_005f: stloc.3
IL_0060: ldloc.3
IL_0061: ret
IL_0057: brfalse.s IL_0064
IL_0059: ldc.i4.2
IL_005a: stloc.3
IL_005b: br.s IL_0066
IL_005d: ldarg.2
IL_005e: brfalse.s IL_0064
IL_0060: ldc.i4.1
IL_0061: stloc.3
IL_0062: br.s IL_0066
IL_0064: ldarg.0
IL_0065: stloc.3
IL_0066: ldloc.3
IL_0067: ret
}"
);
}
}
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
浏览文件 @
cec28aa3
此差异已折叠。
点击以展开。
src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
浏览文件 @
cec28aa3
...
...
@@ -2216,21 +2216,20 @@ .maxstack 2
{
// Code size 61 (0x3d)
.maxstack 2
.locals init ([string] V_0,
string V_1,
string V_2)
.locals init (string V_0,
string V_1)
-IL_0000: nop
-IL_0001: call ""string C.F()""
IL_0006: stloc.1
~IL_0007: ldloc.1
IL_0008: stloc.
2
IL_0009: ldloc.
2
IL_0008: stloc.
0
IL_0009: ldloc.
0
IL_000a: brfalse.s IL_003c
IL_000c: ldloc.
2
IL_000c: ldloc.
0
IL_000d: ldstr ""a""
IL_0012: call ""bool string.op_Equality(string, string)""
IL_0017: brtrue.s IL_0028
IL_0019: ldloc.
2
IL_0019: ldloc.
0
IL_001a: ldstr ""b""
IL_001f: call ""bool string.op_Equality(string, string)""
IL_0024: brtrue.s IL_0032
...
...
@@ -2323,7 +2322,7 @@ .maxstack 2
<customDebugInfo>
<forward declaringType=""C"" methodName=""F"" />
<encLocalSlotMap>
<slot kind=""35"" offset=""1
9
"" />
<slot kind=""35"" offset=""1
1
"" />
<slot kind=""1"" offset=""11"" />
</encLocalSlotMap>
</customDebugInfo>
...
...
@@ -2354,19 +2353,18 @@ .maxstack 2
{
// Code size 42 (0x2a)
.maxstack 2
.locals init ([int] V_0,
int V_1,
int V_2)
.locals init (int V_0,
int V_1)
IL_0000: nop
IL_0001: call ""int C.F()""
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: stloc.
2
IL_0009: ldloc.
2
IL_0008: stloc.
0
IL_0009: ldloc.
0
IL_000a: ldc.i4.1
IL_000b: beq.s IL_0015
IL_000d: br.s IL_000f
IL_000f: ldloc.
2
IL_000f: ldloc.
0
IL_0010: ldc.i4.2
IL_0011: beq.s IL_001f
IL_0013: br.s IL_0029
...
...
@@ -2424,9 +2422,6 @@ static void M()
<slot kind=""0"" offset=""162"" />
<slot kind=""0"" offset=""273"" />
<slot kind=""0"" offset=""323"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""1"" offset=""11"" />
</encLocalSlotMap>
</customDebugInfo>
...
...
@@ -2436,86 +2431,77 @@ static void M()
v0
.
VerifyIL
(
"C.M"
,
@"
{
// Code size 1
67 (0xa7
)
// Code size 1
47 (0x93
)
.maxstack 2
.locals init (byte V_0, //b
int V_1, //i
int V_2, //j
object V_3, //o
int V_4,
byte V_5,
object V_6,
object V_7)
object V_4)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_6
IL_000c: ldloc.s V_6
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_6
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_4
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_4
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_6
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_6
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_5
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_5
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_6
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_5
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
IL_005d: br.s IL_0039
IL_005f: ldloc.0
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_4
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
IL_0072: br.s IL_008a
IL_0074: ldloc.1
IL_0075: call ""void System.Console.WriteLine(int)""
IL_007a: nop
IL_007b: br.s IL_00a6
IL_007d: ldstr ""byte 1""
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_4
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_6
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
IL_009e: call ""void System.Console.WriteLine(object)""
IL_00a3: nop
IL_00a4: br.s IL_00a6
IL_00a6: ret
IL_0006: stloc.s V_4
IL_0008: ldloc.s V_4
IL_000a: stloc.3
IL_000b: ldloc.3
IL_000c: isinst ""int""
IL_0011: brfalse.s IL_0020
IL_0013: ldloc.3
IL_0014: unbox.any ""int""
IL_0019: stloc.1
IL_001a: ldc.i4.1
IL_001b: ldloc.1
IL_001c: beq.s IL_003c
IL_001e: br.s IL_005b
IL_0020: ldloc.3
IL_0021: isinst ""byte""
IL_0026: brfalse.s IL_0037
IL_0028: ldloc.3
IL_0029: unbox.any ""byte""
IL_002e: stloc.0
IL_002f: br.s IL_0049
IL_0031: ldc.i4.1
IL_0032: ldloc.0
IL_0033: beq.s IL_006d
IL_0035: br.s IL_0087
IL_0037: ldloc.3
IL_0038: brtrue.s IL_0087
IL_003a: br.s IL_0092
IL_003c: ldstr ""int 1""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: nop
IL_0047: br.s IL_0092
IL_0049: call ""bool C.P()""
IL_004e: brtrue.s IL_0052
IL_0050: br.s IL_0031
IL_0052: ldloc.0
IL_0053: call ""void System.Console.WriteLine(int)""
IL_0058: nop
IL_0059: br.s IL_0092
IL_005b: call ""bool C.P()""
IL_0060: brtrue.s IL_0064
IL_0062: br.s IL_007a
IL_0064: ldloc.1
IL_0065: call ""void System.Console.WriteLine(int)""
IL_006a: nop
IL_006b: br.s IL_0092
IL_006d: ldstr ""byte 1""
IL_0072: call ""void System.Console.WriteLine(string)""
IL_0077: nop
IL_0078: br.s IL_0092
IL_007a: ldloc.1
IL_007b: stloc.2
IL_007c: br.s IL_007e
IL_007e: ldloc.2
IL_007f: call ""void System.Console.WriteLine(int)""
IL_0084: nop
IL_0085: br.s IL_0092
IL_0087: br.s IL_0089
IL_0089: ldloc.3
IL_008a: call ""void System.Console.WriteLine(object)""
IL_008f: nop
IL_0090: br.s IL_0092
IL_0092: ret
}"
);
var
methodData0
=
v0
.
TestData
.
GetMethodData
(
"C.M"
);
var
method0
=
compilation0
.
GetMember
<
MethodSymbol
>(
"C.M"
);
...
...
@@ -2528,89 +2514,77 @@ .maxstack 2
diff1
.
VerifyIL
(
"C.M"
,
@"
{
// Code size 1
67 (0xa7
)
// Code size 1
47 (0x93
)
.maxstack 2
.locals init (byte V_0, //b
int V_1, //i
int V_2, //j
object V_3, //o
[int] V_4,
[unchanged] V_5,
[object] V_6,
object V_7,
int V_8,
byte V_9,
object V_10)
object V_4)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_10
IL_000c: ldloc.s V_10
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_10
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_8
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_8
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_10
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_10
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_9
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_9
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_10
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_9
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
IL_005d: br.s IL_0039
IL_005f: ldloc.0
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_8
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
IL_0072: br.s IL_008a
IL_0074: ldloc.1
IL_0075: call ""void System.Console.WriteLine(int)""
IL_007a: nop
IL_007b: br.s IL_00a6
IL_007d: ldstr ""byte 1""
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_8
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_10
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
IL_009e: call ""void System.Console.WriteLine(object)""
IL_00a3: nop
IL_00a4: br.s IL_00a6
IL_00a6: ret
IL_0006: stloc.s V_4
IL_0008: ldloc.s V_4
IL_000a: stloc.3
IL_000b: ldloc.3
IL_000c: isinst ""int""
IL_0011: brfalse.s IL_0020
IL_0013: ldloc.3
IL_0014: unbox.any ""int""
IL_0019: stloc.1
IL_001a: ldc.i4.1
IL_001b: ldloc.1
IL_001c: beq.s IL_003c
IL_001e: br.s IL_005b
IL_0020: ldloc.3
IL_0021: isinst ""byte""
IL_0026: brfalse.s IL_0037
IL_0028: ldloc.3
IL_0029: unbox.any ""byte""
IL_002e: stloc.0
IL_002f: br.s IL_0049
IL_0031: ldc.i4.1
IL_0032: ldloc.0
IL_0033: beq.s IL_006d
IL_0035: br.s IL_0087
IL_0037: ldloc.3
IL_0038: brtrue.s IL_0087
IL_003a: br.s IL_0092
IL_003c: ldstr ""int 1""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: nop
IL_0047: br.s IL_0092
IL_0049: call ""bool C.P()""
IL_004e: brtrue.s IL_0052
IL_0050: br.s IL_0031
IL_0052: ldloc.0
IL_0053: call ""void System.Console.WriteLine(int)""
IL_0058: nop
IL_0059: br.s IL_0092
IL_005b: call ""bool C.P()""
IL_0060: brtrue.s IL_0064
IL_0062: br.s IL_007a
IL_0064: ldloc.1
IL_0065: call ""void System.Console.WriteLine(int)""
IL_006a: nop
IL_006b: br.s IL_0092
IL_006d: ldstr ""byte 1""
IL_0072: call ""void System.Console.WriteLine(string)""
IL_0077: nop
IL_0078: br.s IL_0092
IL_007a: ldloc.1
IL_007b: stloc.2
IL_007c: br.s IL_007e
IL_007e: ldloc.2
IL_007f: call ""void System.Console.WriteLine(int)""
IL_0084: nop
IL_0085: br.s IL_0092
IL_0087: br.s IL_0089
IL_0089: ldloc.3
IL_008a: call ""void System.Console.WriteLine(object)""
IL_008f: nop
IL_0090: br.s IL_0092
IL_0092: ret
}"
);
}
...
...
src/Compilers/CSharp/Test/Emit/PDB/PDBLambdaTests.cs
浏览文件 @
cec28aa3
...
...
@@ -1250,7 +1250,7 @@ void F()
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""86"" />
<slot kind=""35"" offset=""
94
"" />
<slot kind=""35"" offset=""
86
"" />
<slot kind=""1"" offset=""86"" />
</encLocalSlotMap>
<encLambdaMap>
...
...
@@ -1285,8 +1285,7 @@ void F()
</scope>
</method>
</methods>
</symbols>
"
);
</symbols>"
);
}
[
Fact
]
...
...
src/Compilers/CSharp/Test/Emit/PDB/PDBLocalFunctionTests.cs
浏览文件 @
cec28aa3
...
...
@@ -331,7 +331,7 @@ void F()
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""75"" />
<slot kind=""35"" offset=""
83
"" />
<slot kind=""35"" offset=""
75
"" />
<slot kind=""1"" offset=""75"" />
</encLocalSlotMap>
<encLambdaMap>
...
...
src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
浏览文件 @
cec28aa3
...
...
@@ -2790,7 +2790,7 @@ static void Main(string[] args)
<encLocalSlotMap>
<slot kind=""5"" offset=""15"" />
<slot kind=""0"" offset=""15"" />
<slot kind=""35"" offset=""
91
"" />
<slot kind=""35"" offset=""
83
"" />
<slot kind=""1"" offset=""83"" />
<slot kind=""1"" offset=""237"" />
</encLocalSlotMap>
...
...
@@ -3022,9 +3022,7 @@ class Student : Person { public double GPA; }
<slot kind=""0"" offset=""59"" />
<slot kind=""0"" offset=""163"" />
<slot kind=""0"" offset=""250"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
...
...
@@ -3033,21 +3031,21 @@ class Student : Person { public double GPA; }
<entry offset=""0x0"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" document=""1"" />
<entry offset=""0x4"" hidden=""true"" document=""1"" />
<entry offset=""0x3
7
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""57"" document=""1"" />
<entry offset=""0x5
a
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""57"" document=""1"" />
<entry offset=""0x7
e
"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""59"" document=""1"" />
<entry offset=""0x
98
"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""43"" document=""1"" />
<entry offset=""0xa
c
"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xa
f
"">
<scope startOffset=""0x
22"" endOffset=""0x56
"">
<local name=""s"" il_index=""0"" il_start=""0x
22"" il_end=""0x56
"" attributes=""0"" />
<entry offset=""0x3
0
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""57"" document=""1"" />
<entry offset=""0x5
3
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""57"" document=""1"" />
<entry offset=""0x7
4
"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""59"" document=""1"" />
<entry offset=""0x
8e
"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""43"" document=""1"" />
<entry offset=""0xa
2
"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xa
5
"">
<scope startOffset=""0x
1d"" endOffset=""0x4f
"">
<local name=""s"" il_index=""0"" il_start=""0x
1d"" il_end=""0x4f
"" attributes=""0"" />
</scope>
<scope startOffset=""0x
56"" endOffset=""0x79
"">
<local name=""s"" il_index=""1"" il_start=""0x
56"" il_end=""0x79
"" attributes=""0"" />
<scope startOffset=""0x
4f"" endOffset=""0x72
"">
<local name=""s"" il_index=""1"" il_start=""0x
4f"" il_end=""0x72
"" attributes=""0"" />
</scope>
<scope startOffset=""0x7
9"" endOffset=""0x98
"">
<local name=""t"" il_index=""2"" il_start=""0x7
9"" il_end=""0x98
"" attributes=""0"" />
<scope startOffset=""0x7
2"" endOffset=""0x8e
"">
<local name=""t"" il_index=""2"" il_start=""0x7
2"" il_end=""0x8e
"" attributes=""0"" />
</scope>
</scope>
</method>
...
...
@@ -3109,9 +3107,7 @@ class Student : Person { public double GPA; }
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""11"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
...
...
@@ -3129,17 +3125,17 @@ class Student : Person { public double GPA; }
<entry offset=""0x0"" hidden=""true"" document=""1"" />
<entry offset=""0xd"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" document=""1"" />
<entry offset=""0xe"" hidden=""true"" document=""1"" />
<entry offset=""0x1
c
"" hidden=""true"" document=""1"" />
<entry offset=""0x5
7
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""63"" document=""1"" />
<entry offset=""0x7
0
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""63"" document=""1"" />
<entry offset=""0x8
9
"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""65"" document=""1"" />
<entry offset=""0x9
9
"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""49"" document=""1"" />
<entry offset=""0xa
9
"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x
ac
"">
<local name=""CS$<>8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0x
ac
"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xa
9
"">
<local name=""CS$<>8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xa
9
"" attributes=""0"" />
<entry offset=""0x1
b
"" hidden=""true"" document=""1"" />
<entry offset=""0x5
f
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""63"" document=""1"" />
<entry offset=""0x7
d
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""63"" document=""1"" />
<entry offset=""0x8
f
"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""65"" document=""1"" />
<entry offset=""0x9
f
"" startLine=""29"" startColumn=""17"" endLine=""29"" endColumn=""49"" document=""1"" />
<entry offset=""0xa
f
"" startLine=""31"" startColumn=""5"" endLine=""31"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x
b2
"">
<local name=""CS$<>8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0x
b2
"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0xa
f
"">
<local name=""CS$<>8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0xa
f
"" attributes=""0"" />
</scope>
</scope>
</method>
...
...
@@ -3205,9 +3201,7 @@ class Student : Person { public double GPA; }
<encLocalSlotMap>
<slot kind=""30"" offset=""0"" />
<slot kind=""30"" offset=""11"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""19"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
...
...
@@ -3225,23 +3219,22 @@ class Student : Person { public double GPA; }
<entry offset=""0x0"" hidden=""true"" document=""1"" />
<entry offset=""0xd"" startLine=""19"" startColumn=""5"" endLine=""19"" endColumn=""6"" document=""1"" />
<entry offset=""0xe"" hidden=""true"" document=""1"" />
<entry offset=""0x1
c
"" hidden=""true"" document=""1"" />
<entry offset=""0x
58
"" startLine=""24"" startColumn=""17"" endLine=""24"" endColumn=""27"" document=""1"" />
<entry offset=""0x7
2
"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""27"" document=""1"" />
<entry offset=""0x
8c
"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""27"" document=""1"" />
<entry offset=""0x
9d
"" startLine=""33"" startColumn=""17"" endLine=""33"" endColumn=""27"" document=""1"" />
<entry offset=""0x
ad
"" startLine=""35"" startColumn=""5"" endLine=""35"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xb
0
"">
<local name=""CS$<>8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xb
0
"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0x
ad
"">
<local name=""CS$<>8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0x
ad
"" attributes=""0"" />
<entry offset=""0x1
b
"" hidden=""true"" document=""1"" />
<entry offset=""0x
60
"" startLine=""24"" startColumn=""17"" endLine=""24"" endColumn=""27"" document=""1"" />
<entry offset=""0x7
f
"" startLine=""27"" startColumn=""17"" endLine=""27"" endColumn=""27"" document=""1"" />
<entry offset=""0x
92
"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""27"" document=""1"" />
<entry offset=""0x
a3
"" startLine=""33"" startColumn=""17"" endLine=""33"" endColumn=""27"" document=""1"" />
<entry offset=""0x
b3
"" startLine=""35"" startColumn=""5"" endLine=""35"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0xb
6
"">
<local name=""CS$<>8__locals0"" il_index=""0"" il_start=""0x0"" il_end=""0xb
6
"" attributes=""0"" />
<scope startOffset=""0xe"" endOffset=""0x
b3
"">
<local name=""CS$<>8__locals1"" il_index=""1"" il_start=""0xe"" il_end=""0x
b3
"" attributes=""0"" />
</scope>
</scope>
</method>
</methods>
</symbols>
"
);
</symbols>"
);
}
[
Fact
,
WorkItem
(
17090
,
"https://github.com/dotnet/roslyn/issues/17090"
),
WorkItem
(
19731
,
"https://github.com/dotnet/roslyn/issues/19731"
)]
...
...
@@ -7142,7 +7135,7 @@ public void SyntaxOffset_Pattern()
</using>
<encLocalSlotMap>
<slot kind=""0"" offset=""12"" />
<slot kind=""35"" offset=""
22
"" />
<slot kind=""35"" offset=""
17
"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
...
...
@@ -8310,14 +8303,16 @@ static void M(object o)
var
c
=
CreateCompilationWithMscorlib40AndSystemCore
(
source
,
options
:
TestOptions
.
DebugDll
);
CompileAndVerify
(
c
).
VerifyIL
(
"Program.M"
,
@"{
// Code size 12
2 (0x7a
)
// Code size 12
3 (0x7b
)
.maxstack 2
.locals init (object V_0,
int V_1,
object V_2,
int V_3,
object V_4,
object V_5)
object V_3,
int V_4,
object V_5,
object V_6,
object V_7)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.2
...
...
@@ -8356,28 +8351,28 @@ .maxstack 2
IL_004a: br.s IL_004e
IL_004c: br.s IL_004e
IL_004e: ldarg.0
IL_004f: stloc.s V_
4
IL_0051: ldloc.s V_
4
IL_0053: st
arg.s V_0
IL_005
5: ldarg.0
IL_005
6
: isinst ""int""
IL_005
b: brfalse.s IL_006c
IL_005
d: ldarg.0
IL_005
e
: unbox.any ""int""
IL_006
3: stloc.3
IL_004f: stloc.s V_
5
IL_0051: ldloc.s V_
5
IL_0053: st
loc.3
IL_005
4: ldloc.3
IL_005
5
: isinst ""int""
IL_005
a: brfalse.s IL_006d
IL_005
c: ldloc.3
IL_005
d
: unbox.any ""int""
IL_006
2: stloc.s V_4
IL_0064: ldc.i4.1
IL_0065: ldloc.
3
IL_006
6: beq.s IL_006a
IL_006
8: br.s IL_006c
IL_006
a: br.s IL_006e
IL_006
c: br.s IL_006e
IL_006
e
: ldarg.0
IL_00
6f: stloc.s V_5
IL_007
1: ldloc.s V_5
IL_007
3: starg.s V_0
IL_007
5: br.s IL_0077
IL_007
7: br.s IL_0079
IL_007
9
: ret
IL_0065: ldloc.
s V_4
IL_006
7: beq.s IL_006b
IL_006
9: br.s IL_006d
IL_006
b: br.s IL_006f
IL_006
d: br.s IL_006f
IL_006
f
: ldarg.0
IL_00
70: stloc.s V_7
IL_007
2: ldloc.s V_7
IL_007
4: stloc.s V_6
IL_007
6: br.s IL_0078
IL_007
8: br.s IL_007a
IL_007
a
: ret
}"
);
c
.
VerifyPdb
(
@"<symbols>
...
...
@@ -8391,11 +8386,13 @@ .maxstack 2
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""35"" offset=""1
9
"" />
<slot kind=""35"" offset=""1
9
"" />
<slot kind=""35"" offset=""1
1
"" />
<slot kind=""35"" offset=""1
1
"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""35"" offset=""386"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""1"" offset=""378"" />
<slot kind=""35"" offset=""511"" />
<slot kind=""1"" offset=""511"" />
</encLocalSlotMap>
</customDebugInfo>
...
...
@@ -8409,12 +8406,12 @@ .maxstack 2
<entry offset=""0x4c"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" document=""1"" />
<entry offset=""0x4e"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" document=""1"" />
<entry offset=""0x51"" hidden=""true"" document=""1"" />
<entry offset=""0x6
a
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" document=""1"" />
<entry offset=""0x6
c
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" document=""1"" />
<entry offset=""0x6
e
"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" document=""1"" />
<entry offset=""0x7
1
"" hidden=""true"" document=""1"" />
<entry offset=""0x7
7
"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" document=""1"" />
<entry offset=""0x7
9
"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" document=""1"" />
<entry offset=""0x6
b
"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" document=""1"" />
<entry offset=""0x6
d
"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" document=""1"" />
<entry offset=""0x6
f
"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" document=""1"" />
<entry offset=""0x7
2
"" hidden=""true"" document=""1"" />
<entry offset=""0x7
8
"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" document=""1"" />
<entry offset=""0x7
a
"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" document=""1"" />
</sequencePoints>
</method>
</methods>
...
...
@@ -8541,10 +8538,10 @@ .maxstack 2
IL_0016: stloc.1
// sequence point: <hidden>
IL_0017: ldloc.1
IL_0018: stfld ""int Program.<Test>d__0.<
i>5__1
""
IL_0018: stfld ""int Program.<Test>d__0.<
>s__2
""
IL_001d: ldc.i4.1
IL_001e: ldarg.0
IL_001f: ldfld ""int Program.<Test>d__0.<
i>5__1
""
IL_001f: ldfld ""int Program.<Test>d__0.<
>s__2
""
IL_0024: beq.s IL_0028
IL_0026: br.s IL_002a
// sequence point: break;
...
...
src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs
浏览文件 @
cec28aa3
...
...
@@ -5513,9 +5513,9 @@ public struct Single
compilation
.
VerifyDiagnostics
(
);
compilation
.
GetEmitDiagnostics
().
Where
(
d
=>
d
.
Severity
!=
DiagnosticSeverity
.
Warning
).
Verify
(
// (
5,17
): error CS0656: Missing compiler required member 'System.Single.IsNaN'
//
switch (o)
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"
o"
).
WithArguments
(
"System.Single"
,
"IsNaN"
).
WithLocation
(
5
,
17
)
// (
7,13
): error CS0656: Missing compiler required member 'System.Single.IsNaN'
//
case 0f/0f: break;
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"
case 0f/0f:"
).
WithArguments
(
"System.Single"
,
"IsNaN"
).
WithLocation
(
7
,
13
)
);
}
...
...
src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs
浏览文件 @
cec28aa3
...
...
@@ -2080,9 +2080,9 @@ public static void M(object o)
// (13,13): error CS0656: Missing compiler required member 'System.String.op_Equality'
// case "hmm":
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
@"case ""hmm"":"
).
WithArguments
(
"System.String"
,
"op_Equality"
).
WithLocation
(
13
,
13
),
// (
11,17
): error CS0656: Missing compiler required member 'System.String.op_Equality'
//
switch (o)
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
"o"
).
WithArguments
(
"System.String"
,
"op_Equality"
).
WithLocation
(
11
,
17
)
// (
33,13
): error CS0656: Missing compiler required member 'System.String.op_Equality'
//
case "baz":
Diagnostic
(
ErrorCode
.
ERR_MissingPredefinedMember
,
@"case ""baz"":"
).
WithArguments
(
"System.String"
,
"op_Equality"
).
WithLocation
(
33
,
13
)
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录