Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
6ec8f3ab
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,发现更多精彩内容 >>
提交
6ec8f3ab
编写于
2月 17, 2017
作者:
A
AlekseyTs
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add support for deconstruction into field-like events.
Fixes #16962.
上级
773f3c91
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
305 addition
and
26 deletion
+305
-26
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs
...owering/LocalRewriter/LocalRewriter_AssignmentOperator.cs
+22
-3
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs
...LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs
+54
-21
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs
...rp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs
+1
-2
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs
...ilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs
+228
-0
未找到文件。
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs
浏览文件 @
6ec8f3ab
...
...
@@ -18,8 +18,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
private
BoundExpression
VisitAssignmentOperator
(
BoundAssignmentOperator
node
,
bool
used
)
{
var
right
=
node
.
Right
;
var
loweredRight
=
VisitExpression
(
right
);
var
loweredRight
=
VisitExpression
(
node
.
Right
);
BoundExpression
left
=
node
.
Left
;
BoundExpression
loweredLeft
;
...
...
@@ -39,7 +38,7 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
if
(
eventAccess
.
EventSymbol
.
IsWindowsRuntimeEvent
)
{
Debug
.
Assert
(
node
.
RefKind
==
RefKind
.
None
);
return
VisitWindowsRuntimeEventFieldAssignmentOperator
(
node
.
Syntax
,
eventAccess
,
r
ight
);
return
VisitWindowsRuntimeEventFieldAssignmentOperator
(
node
.
Syntax
,
eventAccess
,
loweredR
ight
);
}
goto
default
;
}
...
...
@@ -105,6 +104,26 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
isCompoundAssignment
,
isChecked
).
ToExpression
();
case
BoundKind
.
EventAccess
:
var
eventAccess
=
(
BoundEventAccess
)
rewrittenLeft
;
Debug
.
Assert
(
eventAccess
.
IsUsableAsField
);
if
(
eventAccess
.
EventSymbol
.
IsWindowsRuntimeEvent
)
{
const
bool
isDynamic
=
false
;
return
RewriteWindowsRuntimeEventAssignmentOperator
(
eventAccess
.
Syntax
,
eventAccess
.
EventSymbol
,
EventAssignmentKind
.
Assignment
,
isDynamic
,
eventAccess
.
ReceiverOpt
,
rewrittenRight
);
}
// Only Windows Runtime field-like events can come through here:
// - Assignment operation is not supported for custom (non-field like) events.
// - Access to regular field-like events is expected to be lowered to at least a field access
// when we reach here.
throw
ExceptionUtilities
.
Unreachable
;
default
:
return
MakeStaticAssignmentOperator
(
syntax
,
rewrittenLeft
,
rewrittenRight
,
RefKind
.
None
,
type
,
used
);
}
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs
浏览文件 @
6ec8f3ab
...
...
@@ -120,8 +120,10 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO
return
result
;
}
private
Bound
PropertyAccess
TransformPropertyAccess
(
BoundPropertyAccess
prop
,
ArrayBuilder
<
BoundExpression
>
stores
,
ArrayBuilder
<
LocalSymbol
>
temps
)
private
Bound
Expression
TransformPropertyOrEventReceiver
(
Symbol
propertyOrEvent
,
BoundExpression
receiverOpt
,
ArrayBuilder
<
BoundExpression
>
stores
,
ArrayBuilder
<
LocalSymbol
>
temps
)
{
Debug
.
Assert
(
propertyOrEvent
.
Kind
==
SymbolKind
.
Property
||
propertyOrEvent
.
Kind
==
SymbolKind
.
Event
);
// We need to stash away the receiver so that it does not get evaluated twice.
// If the receiver is classified as a value of reference type then we can simply say
//
...
...
@@ -141,14 +143,14 @@ private BoundPropertyAccess TransformPropertyAccess(BoundPropertyAccess prop, Ar
// assume that was the case.
// If the property is static or if the receiver is of kind "Base" or "this", then we can just generate prop = prop + value
if
(
prop
.
ReceiverOpt
==
null
||
prop
.
PropertySymbol
.
IsStatic
||
!
CanChangeValueBetweenReads
(
prop
.
R
eceiverOpt
))
if
(
receiverOpt
==
null
||
propertyOrEvent
.
IsStatic
||
!
CanChangeValueBetweenReads
(
r
eceiverOpt
))
{
return
prop
;
return
receiverOpt
;
}
Debug
.
Assert
(
prop
.
R
eceiverOpt
.
Kind
!=
BoundKind
.
TypeExpression
);
Debug
.
Assert
(
r
eceiverOpt
.
Kind
!=
BoundKind
.
TypeExpression
);
BoundExpression
rewrittenReceiver
=
VisitExpression
(
prop
.
R
eceiverOpt
);
BoundExpression
rewrittenReceiver
=
VisitExpression
(
r
eceiverOpt
);
BoundAssignmentOperator
assignmentToTemp
;
...
...
@@ -165,9 +167,7 @@ private BoundPropertyAccess TransformPropertyAccess(BoundPropertyAccess prop, Ar
stores
.
Add
(
assignmentToTemp
);
temps
.
Add
(
receiverTemp
.
LocalSymbol
);
// CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
// Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of properties.
return
new
BoundPropertyAccess
(
prop
.
Syntax
,
receiverTemp
,
prop
.
PropertySymbol
,
prop
.
ResultKind
,
prop
.
Type
);
return
receiverTemp
;
}
private
BoundDynamicMemberAccess
TransformDynamicMemberAccess
(
BoundDynamicMemberAccess
memberAccess
,
ArrayBuilder
<
BoundExpression
>
stores
,
ArrayBuilder
<
LocalSymbol
>
temps
)
...
...
@@ -318,8 +318,7 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce
argumentRefKinds
=
GetRefKindsOrNull
(
refKinds
);
refKinds
.
Free
();
// CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
// Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers.
// This is a temporary object that will be rewritten away before the lowering completes.
return
new
BoundIndexerAccess
(
syntax
,
transformedReceiver
,
...
...
@@ -332,15 +331,31 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce
indexerAccess
.
Type
);
}
private
BoundFieldAccess
TransformReferenceTypeFieldAccess
(
BoundFieldAccess
fieldAccess
,
BoundExpression
receiver
,
ArrayBuilder
<
BoundExpression
>
stores
,
ArrayBuilder
<
LocalSymbol
>
temps
)
/// <summary>
/// Returns true if the <paramref name="receiver"/> was lowered and transformed.
/// The <paramref name="receiver"/> is not changed if this function returns false.
/// </summary>
private
bool
TransformCompoundAssignmentFieldOrEventAccessReceiver
(
Symbol
fieldOrEvent
,
ref
BoundExpression
receiver
,
ArrayBuilder
<
BoundExpression
>
stores
,
ArrayBuilder
<
LocalSymbol
>
temps
)
{
Debug
.
Assert
(
fieldOrEvent
.
Kind
==
SymbolKind
.
Field
||
fieldOrEvent
.
Kind
==
SymbolKind
.
Event
);
//If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
if
(
fieldOrEvent
.
IsStatic
||
!
CanChangeValueBetweenReads
(
receiver
))
{
return
true
;
}
else
if
(!
receiver
.
Type
.
IsReferenceType
)
{
return
false
;
}
Debug
.
Assert
(
receiver
.
Type
.
IsReferenceType
);
Debug
.
Assert
(
receiver
.
Kind
!=
BoundKind
.
TypeExpression
);
BoundExpression
rewrittenReceiver
=
VisitExpression
(
receiver
);
if
(
rewrittenReceiver
.
Type
.
IsTypeParameter
())
{
var
memberContainingType
=
field
Access
.
FieldSymbol
.
ContainingType
;
var
memberContainingType
=
field
OrEvent
.
ContainingType
;
// From the verifier prospective type parameters do not contain fields or methods.
// the instance must be "boxed" to access the field
...
...
@@ -352,7 +367,8 @@ private BoundFieldAccess TransformReferenceTypeFieldAccess(BoundFieldAccess fiel
var
receiverTemp
=
_factory
.
StoreToTemp
(
rewrittenReceiver
,
out
assignmentToTemp
);
stores
.
Add
(
assignmentToTemp
);
temps
.
Add
(
receiverTemp
.
LocalSymbol
);
return
new
BoundFieldAccess
(
fieldAccess
.
Syntax
,
receiverTemp
,
fieldAccess
.
FieldSymbol
,
null
);
receiver
=
receiverTemp
;
return
true
;
}
private
BoundDynamicIndexerAccess
TransformDynamicIndexerAccess
(
BoundDynamicIndexerAccess
indexerAccess
,
ArrayBuilder
<
BoundExpression
>
stores
,
ArrayBuilder
<
LocalSymbol
>
temps
)
...
...
@@ -452,7 +468,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
var
propertyAccess
=
(
BoundPropertyAccess
)
originalLHS
;
if
(
propertyAccess
.
PropertySymbol
.
RefKind
==
RefKind
.
None
)
{
return
TransformPropertyAccess
(
propertyAccess
,
stores
,
temps
);
// This is a temporary object that will be rewritten away before the lowering completes.
return
propertyAccess
.
Update
(
TransformPropertyOrEventReceiver
(
propertyAccess
.
PropertySymbol
,
propertyAccess
.
ReceiverOpt
,
stores
,
temps
),
propertyAccess
.
PropertySymbol
,
propertyAccess
.
ResultKind
,
propertyAccess
.
Type
);
}
}
break
;
...
...
@@ -479,14 +497,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
var
fieldAccess
=
(
BoundFieldAccess
)
originalLHS
;
BoundExpression
receiverOpt
=
fieldAccess
.
ReceiverOpt
;
//If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
if
(
fieldAccess
.
FieldSymbol
.
IsStatic
||
!
CanChangeValueBetweenReads
(
receiverOpt
))
{
return
fieldAccess
;
}
else
if
(
receiverOpt
.
Type
.
IsReferenceType
)
if
(
TransformCompoundAssignmentFieldOrEventAccessReceiver
(
fieldAccess
.
FieldSymbol
,
ref
receiverOpt
,
stores
,
temps
))
{
return
TransformReferenceTypeFieldAccess
(
fieldAccess
,
receiverOpt
,
stores
,
temp
s
);
return
MakeFieldAccess
(
fieldAccess
.
Syntax
,
receiverOpt
,
fieldAccess
.
FieldSymbol
,
fieldAccess
.
ConstantValueOpt
,
fieldAccess
.
ResultKind
,
fieldAccess
.
Type
,
fieldAcces
s
);
}
}
break
;
...
...
@@ -544,6 +557,26 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
case
BoundKind
.
RefValueOperator
:
break
;
case
BoundKind
.
EventAccess
:
{
var
eventAccess
=
(
BoundEventAccess
)
originalLHS
;
Debug
.
Assert
(
eventAccess
.
IsUsableAsField
);
BoundExpression
receiverOpt
=
eventAccess
.
ReceiverOpt
;
if
(
eventAccess
.
EventSymbol
.
IsWindowsRuntimeEvent
)
{
// This is a temporary object that will be rewritten away before the lowering completes.
return
eventAccess
.
Update
(
TransformPropertyOrEventReceiver
(
eventAccess
.
EventSymbol
,
eventAccess
.
ReceiverOpt
,
stores
,
temps
),
eventAccess
.
EventSymbol
,
eventAccess
.
IsUsableAsField
,
eventAccess
.
ResultKind
,
eventAccess
.
Type
);
}
if
(
TransformCompoundAssignmentFieldOrEventAccessReceiver
(
eventAccess
.
EventSymbol
,
ref
receiverOpt
,
stores
,
temps
))
{
return
MakeEventAccess
(
eventAccess
.
Syntax
,
receiverOpt
,
eventAccess
.
EventSymbol
,
eventAccess
.
ConstantValue
,
eventAccess
.
ResultKind
,
eventAccess
.
Type
);
}
}
break
;
default
:
throw
ExceptionUtilities
.
UnexpectedValue
(
originalLHS
.
Kind
);
}
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs
浏览文件 @
6ec8f3ab
...
...
@@ -172,7 +172,7 @@ private BoundExpression RewriteWindowsRuntimeEventAssignmentOperator(SyntaxNode
return
new
BoundSequence
(
syntax
,
tempSymbols
,
sideEffects
.
ToImmutableAndFree
(),
marshalCall
,
marshalCall
.
Type
);
}
private
BoundExpression
VisitWindowsRuntimeEventFieldAssignmentOperator
(
SyntaxNode
syntax
,
BoundEventAccess
left
,
BoundExpression
right
)
private
BoundExpression
VisitWindowsRuntimeEventFieldAssignmentOperator
(
SyntaxNode
syntax
,
BoundEventAccess
left
,
BoundExpression
r
ewrittenR
ight
)
{
Debug
.
Assert
(
left
.
IsUsableAsField
);
...
...
@@ -181,7 +181,6 @@ private BoundExpression VisitWindowsRuntimeEventFieldAssignmentOperator(SyntaxNo
Debug
.
Assert
(
eventSymbol
.
IsWindowsRuntimeEvent
);
BoundExpression
rewrittenReceiverOpt
=
left
.
ReceiverOpt
==
null
?
null
:
VisitExpression
(
left
.
ReceiverOpt
);
BoundExpression
rewrittenRight
=
VisitExpression
(
right
);
const
bool
isDynamic
=
false
;
return
RewriteWindowsRuntimeEventAssignmentOperator
(
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs
浏览文件 @
6ec8f3ab
...
...
@@ -6159,7 +6159,235 @@ public static void Main()
// (9,18): error CS8186: A foreach loop must declare its iteration variables.
// foreach ((arr[out int size], int b) in data) {}
Diagnostic
(
ErrorCode
.
ERR_MustDeclareForeachIteration
,
"(arr[out int size], int b)"
).
WithLocation
(
9
,
18
)
);
}
[
Fact
]
[
WorkItem
(
16962
,
"https://github.com/dotnet/roslyn/issues/16962"
)]
public
void
Events_01
()
{
string
source
=
@"
class C
{
static event System.Action E;
static void Main()
{
(E, _) = (null, 1);
System.Console.WriteLine(E == null);
(E, _) = (Handler, 1);
E();
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
"
;
var
comp
=
CompileAndVerify
(
source
,
expectedOutput
:
@"True
Handler"
,
additionalRefs
:
s_valueTupleRefs
);
comp
.
VerifyDiagnostics
();
}
[
Fact
]
[
WorkItem
(
16962
,
"https://github.com/dotnet/roslyn/issues/16962"
)]
public
void
Events_02
()
{
string
source
=
@"
struct S
{
event System.Action E;
class C
{
static void Main()
{
var s = new S();
(s.E, _) = (null, 1);
System.Console.WriteLine(s.E == null);
(s.E, _) = (Handler, 1);
s.E();
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
}
"
;
var
comp
=
CompileAndVerify
(
source
,
expectedOutput
:
@"True
Handler"
,
additionalRefs
:
s_valueTupleRefs
);
comp
.
VerifyDiagnostics
();
}
[
Fact
]
[
WorkItem
(
16962
,
"https://github.com/dotnet/roslyn/issues/16962"
)]
public
void
Events_03
()
{
string
source1
=
@"
public interface EventInterface
{
event System.Action E;
}
"
;
var
comp1
=
CreateCompilation
(
source1
,
WinRtRefs
,
TestOptions
.
ReleaseWinMD
,
TestOptions
.
Regular
);
string
source2
=
@"
class C : EventInterface
{
public event System.Action E;
static void Main()
{
var c = new C();
c.Test();
}
void Test()
{
(E, _) = (null, 1);
System.Console.WriteLine(E == null);
(E, _) = (Handler, 1);
E();
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
"
;
var
comp2
=
CompileAndVerify
(
source2
,
expectedOutput
:
@"True
Handler"
,
additionalRefs
:
WinRtRefs
.
Concat
(
new
[]
{
SystemRuntimeFacadeRef
,
ValueTupleRef
,
comp1
.
ToMetadataReference
()
}));
comp2
.
VerifyDiagnostics
();
Assert
.
True
(
comp2
.
Compilation
.
GetMember
<
EventSymbol
>(
"C.E"
).
IsWindowsRuntimeEvent
);
}
[
Fact
]
[
WorkItem
(
16962
,
"https://github.com/dotnet/roslyn/issues/16962"
)]
public
void
Events_04
()
{
string
source1
=
@"
public interface EventInterface
{
event System.Action E;
}
"
;
var
comp1
=
CreateCompilation
(
source1
,
WinRtRefs
,
TestOptions
.
ReleaseWinMD
,
TestOptions
.
Regular
);
string
source2
=
@"
struct S : EventInterface
{
public event System.Action E;
class C
{
S s = new S();
static void Main()
{
var c = new C();
(GetC(c).s.E, _) = (null, GetInt(1));
System.Console.WriteLine(c.s.E == null);
(GetC(c).s.E, _) = (Handler, GetInt(2));
c.s.E();
}
static int GetInt(int i)
{
System.Console.WriteLine(i);
return i;
}
static C GetC(C c)
{
System.Console.WriteLine(""GetC"");
return c;
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
}
"
;
var
comp2
=
CompileAndVerify
(
source2
,
expectedOutput
:
@"GetC
1
True
GetC
2
Handler"
,
additionalRefs
:
WinRtRefs
.
Concat
(
new
[]
{
SystemRuntimeFacadeRef
,
ValueTupleRef
,
comp1
.
ToMetadataReference
()
}));
comp2
.
VerifyDiagnostics
();
Assert
.
True
(
comp2
.
Compilation
.
GetMember
<
EventSymbol
>(
"S.E"
).
IsWindowsRuntimeEvent
);
}
[
Fact
]
[
WorkItem
(
16962
,
"https://github.com/dotnet/roslyn/issues/16962"
)]
public
void
Events_05
()
{
string
source
=
@"
class C
{
public static event System.Action E;
}
class Program
{
static void Main()
{
(C.E, _) = (null, 1);
}
}
"
;
var
compilation
=
CreateCompilationWithMscorlib
(
source
,
references
:
s_valueTupleRefs
);
compilation
.
VerifyDiagnostics
(
// (11,12): error CS0070: The event 'C.E' can only appear on the left hand side of += or -= (except when used from within the type 'C')
// (C.E, _) = (null, 1);
Diagnostic
(
ErrorCode
.
ERR_BadEventUsage
,
"E"
).
WithArguments
(
"C.E"
,
"C"
).
WithLocation
(
11
,
12
)
);
}
[
Fact
]
[
WorkItem
(
16962
,
"https://github.com/dotnet/roslyn/issues/16962"
)]
public
void
Events_06
()
{
string
source
=
@"
class C
{
static event System.Action E
{
add {}
remove {}
}
static void Main()
{
(E, _) = (null, 1);
}
}
"
;
var
compilation
=
CreateCompilationWithMscorlib
(
source
,
references
:
s_valueTupleRefs
);
compilation
.
VerifyDiagnostics
(
// (12,10): error CS0079: The event 'C.E' can only appear on the left hand side of += or -=
// (E, _) = (null, 1);
Diagnostic
(
ErrorCode
.
ERR_BadEventUsageNoField
,
"E"
).
WithArguments
(
"C.E"
).
WithLocation
(
12
,
10
)
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录