Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
ab63bff3
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,发现更多精彩内容 >>
提交
ab63bff3
编写于
11月 03, 2017
作者:
V
Vladimir Sadov
提交者:
Julien Couvreur
11月 03, 2017
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Enable peverify-compat mode for langver < 7.2 (#22772)
上级
289aebf0
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
360 addition
and
116 deletion
+360
-116
src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
+1
-1
src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
+44
-31
src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
+41
-26
src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
+4
-1
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
...arp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
+1
-1
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs
...le/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs
+4
-2
src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
...ers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
+1
-1
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
...lers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
+195
-0
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
...CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
+36
-52
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs
+33
-1
未找到文件。
src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
浏览文件 @
ab63bff3
...
...
@@ -139,7 +139,7 @@ private bool IsDebugPlus()
private
bool
EnablePEVerifyCompat
()
{
return
_module
.
Compilation
.
FeaturePEVerifyCompatEnabled
;
return
_module
.
Compilation
.
LanguageVersion
<
LanguageVersion
.
CSharp7_2
||
_module
.
Compilation
.
FeaturePEVerifyCompatEnabled
;
}
private
LocalDefinition
LazyReturnTemp
...
...
src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
浏览文件 @
ab63bff3
...
...
@@ -24,8 +24,14 @@ private enum AddressKind
// reference itself will not be written to, nor it will be used to modify fields.
ReadOnly
,
// same as ReadOnly, but we are not supposed to get a reference to a clone
// regardless of compat settings.
ReadOnlyStrict
,
}
private
static
bool
IsReadOnly
(
AddressKind
addressKind
)
=>
addressKind
>=
AddressKind
.
ReadOnly
;
/// <summary>
/// Emits address as in &
///
...
...
@@ -70,7 +76,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
case
BoundKind
.
ThisReference
:
Debug
.
Assert
(
expression
.
Type
.
IsValueType
,
"only value types may need a ref to this"
);
Debug
.
Assert
(
HasHome
(
expression
,
addressKind
==
AddressKind
.
Writeable
));
Debug
.
Assert
(
HasHome
(
expression
,
addressKind
));
_builder
.
EmitOpCode
(
ILOpCode
.
Ldarg_0
);
break
;
...
...
@@ -101,7 +107,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
var
methodRefKind
=
call
.
Method
.
RefKind
;
if
(
methodRefKind
==
RefKind
.
Ref
||
(
addressKind
==
AddressKind
.
ReadOnly
&&
methodRefKind
==
RefKind
.
RefReadOnly
))
(
IsReadOnly
(
addressKind
)
&&
methodRefKind
==
RefKind
.
RefReadOnly
))
{
EmitCallExpression
(
call
,
UseKind
.
UsedAsAddress
);
break
;
...
...
@@ -121,7 +127,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
case
BoundKind
.
ConditionalOperator
:
var
conditional
=
(
BoundConditionalOperator
)
expression
;
if
(!
HasHome
(
conditional
,
addressKind
!=
AddressKind
.
ReadOnly
))
if
(!
HasHome
(
conditional
,
addressKind
))
{
goto
default
;
}
...
...
@@ -144,7 +150,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
return
null
;
default
:
Debug
.
Assert
(!
HasHome
(
expression
,
addressKind
!=
AddressKind
.
ReadOnly
));
Debug
.
Assert
(!
HasHome
(
expression
,
addressKind
));
return
EmitAddressOfTempClone
(
expression
);
}
...
...
@@ -218,7 +224,7 @@ private LocalDefinition EmitLocalAddress(BoundLocal localAccess, AddressKind add
{
var
local
=
localAccess
.
LocalSymbol
;
if
(!
HasHome
(
localAccess
,
needWriteable
:
addressKind
!=
AddressKind
.
ReadOnly
))
if
(!
HasHome
(
localAccess
,
addressKind
))
{
return
EmitAddressOfTempClone
(
localAccess
);
}
...
...
@@ -249,7 +255,7 @@ private LocalDefinition EmitLocalAddress(BoundLocal localAccess, AddressKind add
/// </summary>
private
LocalDefinition
EmitDupAddress
(
BoundDup
dup
,
AddressKind
addressKind
)
{
if
(!
HasHome
(
dup
,
needWriteable
:
addressKind
!=
AddressKind
.
ReadOnly
))
if
(!
HasHome
(
dup
,
addressKind
))
{
return
EmitAddressOfTempClone
(
dup
);
}
...
...
@@ -339,7 +345,7 @@ private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression
/// Checks if expression directly or indirectly represents a value with its own home. In
/// such cases it is possible to get a reference without loading into a temporary.
/// </summary>
private
bool
HasHome
(
BoundExpression
expression
,
bool
needWriteable
)
private
bool
HasHome
(
BoundExpression
expression
,
AddressKind
addressKind
)
{
switch
(
expression
.
Kind
)
{
...
...
@@ -352,10 +358,11 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
case
BoundKind
.
ThisReference
:
Debug
.
Assert
(
expression
.
Type
.
IsValueType
);
if
(
needWriteable
&&
expression
.
Type
.
IsReadOnly
)
if
(
!
IsReadOnly
(
addressKind
)
&&
expression
.
Type
.
IsReadOnly
)
{
return
_method
.
MethodKind
==
MethodKind
.
Constructor
;
}
return
true
;
case
BoundKind
.
ThrowExpression
:
...
...
@@ -363,7 +370,7 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
return
true
;
case
BoundKind
.
Parameter
:
return
!
needWriteable
||
return
IsReadOnly
(
addressKind
)
||
((
BoundParameter
)
expression
).
ParameterSymbol
.
RefKind
!=
RefKind
.
In
;
case
BoundKind
.
Local
:
...
...
@@ -371,31 +378,31 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
// locals in a mutating call
var
local
=
((
BoundLocal
)
expression
).
LocalSymbol
;
return
!((
IsStackLocal
(
local
)
&&
local
.
RefKind
==
RefKind
.
None
)
||
(
needWriteable
&&
local
.
RefKind
==
RefKind
.
RefReadOnly
));
(
!
IsReadOnly
(
addressKind
)
&&
local
.
RefKind
==
RefKind
.
RefReadOnly
));
case
BoundKind
.
Call
:
var
methodRefKind
=
((
BoundCall
)
expression
).
Method
.
RefKind
;
return
methodRefKind
==
RefKind
.
Ref
||
(
!
needWriteable
&&
methodRefKind
==
RefKind
.
RefReadOnly
);
(
IsReadOnly
(
addressKind
)
&&
methodRefKind
==
RefKind
.
RefReadOnly
);
case
BoundKind
.
Dup
:
//NB: Dup represents locals that do not need IL slot
var
dupRefKind
=
((
BoundDup
)
expression
).
RefKind
;
return
dupRefKind
==
RefKind
.
Ref
||
(
!
needWriteable
&&
dupRefKind
==
RefKind
.
RefReadOnly
);
(
IsReadOnly
(
addressKind
)
&&
dupRefKind
==
RefKind
.
RefReadOnly
);
case
BoundKind
.
FieldAccess
:
return
HasHome
((
BoundFieldAccess
)
expression
,
needWriteable
);
return
HasHome
((
BoundFieldAccess
)
expression
,
addressKind
);
case
BoundKind
.
Sequence
:
return
HasHome
(((
BoundSequence
)
expression
).
Value
,
needWriteable
);
return
HasHome
(((
BoundSequence
)
expression
).
Value
,
addressKind
);
case
BoundKind
.
AssignmentOperator
:
return
((
BoundAssignmentOperator
)
expression
).
RefKind
!=
RefKind
.
None
;
case
BoundKind
.
ComplexConditionalReceiver
:
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ValueTypeReceiver
,
needWriteable
));
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ReferenceTypeReceiver
,
needWriteable
));
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ValueTypeReceiver
,
addressKind
));
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ReferenceTypeReceiver
,
addressKind
));
goto
case
BoundKind
.
ConditionalReceiver
;
case
BoundKind
.
ConditionalReceiver
:
...
...
@@ -415,7 +422,7 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
// branch that has no home will need a temporary
// if both have no home, just say whole expression has no home
// so we could just use one temp for the whole thing
return
HasHome
(
ternary
.
Consequence
,
needWriteable
)
&&
HasHome
(
ternary
.
Alternative
,
needWriteable
);
return
HasHome
(
ternary
.
Consequence
,
addressKind
)
&&
HasHome
(
ternary
.
Alternative
,
addressKind
);
default
:
return
false
;
...
...
@@ -427,7 +434,7 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
/// Fields have readable homes when they are not constants.
/// Fields have writeable homes unless they are readonly and used outside of the constructor.
/// </summary>
private
bool
HasHome
(
BoundFieldAccess
fieldAccess
,
bool
needWriteable
)
private
bool
HasHome
(
BoundFieldAccess
fieldAccess
,
AddressKind
addressKind
)
{
FieldSymbol
field
=
fieldAccess
.
FieldSymbol
;
...
...
@@ -437,7 +444,14 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
return
false
;
}
if
(!
needWriteable
&&
!
EnablePEVerifyCompat
())
// in readonly situations where ref to a copy is not allowed, consider fields as addressable
if
(
addressKind
==
AddressKind
.
ReadOnlyStrict
)
{
return
true
;
}
// ReadOnly references can always be taken unless we are in peverify compat mode
if
(
addressKind
==
AddressKind
.
ReadOnly
&&
!
EnablePEVerifyCompat
())
{
return
true
;
}
...
...
@@ -456,7 +470,7 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
// we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways.
if
(!
EnablePEVerifyCompat
())
{
Debug
.
Assert
(
needWriteable
==
true
);
Debug
.
Assert
(
!
IsReadOnly
(
addressKind
)
);
var
receiver
=
fieldAccess
.
ReceiverOpt
;
if
(
receiver
?.
Type
.
IsValueType
==
true
)
...
...
@@ -466,8 +480,8 @@ private bool HasHome(BoundFieldAccess fieldAccess, bool needWriteable)
// has readable home -> return false - we need to copy the field
// otherwise -> return true - the copy will be made at higher level so the leaf field can have writeable home
return
HasHome
(
receiver
,
needWriteable
:
true
)
||
!
HasHome
(
receiver
,
needWriteable
:
false
);
return
HasHome
(
receiver
,
addressKind
)
||
!
HasHome
(
receiver
,
AddressKind
.
ReadOnly
);
}
}
...
...
@@ -534,7 +548,7 @@ private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess, AddressKi
{
FieldSymbol
field
=
fieldAccess
.
FieldSymbol
;
if
(!
HasHome
(
fieldAccess
,
addressKind
!=
AddressKind
.
ReadOnly
))
if
(!
HasHome
(
fieldAccess
,
addressKind
))
{
// accessing a field that is not writable (const or readonly)
return
EmitAddressOfTempClone
(
fieldAccess
);
...
...
@@ -546,11 +560,7 @@ private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess, AddressKi
}
else
{
//NOTE: we are not propagating AddressKind here.
// the reason is that while Constrained permits calls, it does not permit
// taking field addresses, so we have to turn Constrained into writeable.
// It is less error prone to just pass a bool "isReadonly"
return
EmitInstanceFieldAddress
(
fieldAccess
,
isReadonly
:
addressKind
==
AddressKind
.
ReadOnly
);
return
EmitInstanceFieldAddress
(
fieldAccess
,
addressKind
);
}
}
...
...
@@ -564,7 +574,7 @@ private LocalDefinition EmitParameterAddress(BoundParameter parameter, AddressKi
{
ParameterSymbol
parameterSymbol
=
parameter
.
ParameterSymbol
;
if
(!
HasHome
(
parameter
,
needWriteable
:
addressKind
!=
AddressKind
.
ReadOnly
))
if
(!
HasHome
(
parameter
,
addressKind
))
{
// accessing a parameter that is not writable
return
EmitAddressOfTempClone
(
parameter
);
...
...
@@ -634,11 +644,14 @@ private LocalDefinition EmitReceiverRef(BoundExpression receiver, AddressKind ad
/// <summary>
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private
LocalDefinition
EmitInstanceFieldAddress
(
BoundFieldAccess
fieldAccess
,
bool
isReadonly
)
private
LocalDefinition
EmitInstanceFieldAddress
(
BoundFieldAccess
fieldAccess
,
AddressKind
addressKind
)
{
var
field
=
fieldAccess
.
FieldSymbol
;
var
tempOpt
=
EmitReceiverRef
(
fieldAccess
.
ReceiverOpt
,
isReadonly
?
AddressKind
.
ReadOnly
:
AddressKind
.
Writeable
);
//NOTE: we are not propagating AddressKind.Constrained here.
// the reason is that while Constrained permits calls, it does not permit
// taking field addresses, so we have to turn Constrained into writeable.
var
tempOpt
=
EmitReceiverRef
(
fieldAccess
.
ReceiverOpt
,
addressKind
==
AddressKind
.
Constrained
?
AddressKind
.
Writeable
:
addressKind
);
_builder
.
EmitOpCode
(
ILOpCode
.
Ldflda
);
EmitSymbolToken
(
field
,
fieldAccess
.
Syntax
);
...
...
src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
浏览文件 @
ab63bff3
...
...
@@ -426,10 +426,7 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
{
// this does not need to be writeable
// we may call "HasValue" on this, but it is not mutating
// (however verification does not know that and considers all calls mutating)
var
addressKind
=
EnablePEVerifyCompat
()?
AddressKind
.
Writeable
:
AddressKind
.
ReadOnly
;
var
addressKind
=
AddressKind
.
ReadOnly
;
receiverTemp
=
EmitReceiverRef
(
receiver
,
addressKind
);
_builder
.
EmitOpCode
(
ILOpCode
.
Dup
);
...
...
@@ -605,7 +602,9 @@ private void EmitArgument(BoundExpression argument, RefKind refKind)
break
;
default
:
var
unexpectedTemp
=
EmitAddress
(
argument
,
AddressKind
.
Writeable
);
// NOTE: passing "ReadOnlyStrict" here.
// we should not get an address of a copy if at all possible
var
unexpectedTemp
=
EmitAddress
(
argument
,
refKind
==
RefKindExtensions
.
StrictIn
?
AddressKind
.
ReadOnlyStrict
:
AddressKind
.
Writeable
);
if
(
unexpectedTemp
!=
null
)
{
// interestingly enough "ref dynamic" sometimes is passed via a clone
...
...
@@ -619,8 +618,11 @@ private void EmitArgument(BoundExpression argument, RefKind refKind)
private
void
EmitAddressOfExpression
(
BoundAddressOfOperator
expression
,
bool
used
)
{
var
temp
=
EmitAddress
(
expression
.
Operand
,
AddressKind
.
Writeable
);
// NOTE: passing "ReadOnlyStrict" here.
// we should not get an address of a copy if at all possible
var
temp
=
EmitAddress
(
expression
.
Operand
,
AddressKind
.
ReadOnlyStrict
);
Debug
.
Assert
(
temp
==
null
,
"If the operand is addressable, then a temp shouldn't be required."
);
if
(
used
)
{
// When computing an address to be used to initialize a fixed-statement variable, we have to be careful
...
...
@@ -811,15 +813,32 @@ private void EmitSideEffects(BoundSequence sequence)
}
}
private
void
EmitArguments
(
ImmutableArray
<
BoundExpression
>
arguments
,
ImmutableArray
<
ParameterSymbol
>
parameters
)
private
void
EmitArguments
(
ImmutableArray
<
BoundExpression
>
arguments
,
ImmutableArray
<
ParameterSymbol
>
parameters
,
ImmutableArray
<
RefKind
>
refKindsOpt
)
{
// We might have an extra argument for the __arglist() of a varargs method.
Debug
.
Assert
(
arguments
.
Length
==
parameters
.
Length
||
arguments
.
Length
==
parameters
.
Length
+
1
,
"argument count must match parameter count"
);
for
(
int
i
=
0
;
i
<
arguments
.
Length
;
i
++)
{
BoundExpression
argument
=
arguments
[
i
];
RefKind
refKind
=
(
i
==
parameters
.
Length
)
?
RefKind
.
None
:
parameters
[
i
].
RefKind
;
EmitArgument
(
argument
,
refKind
);
RefKind
refKind
;
if
(!
refKindsOpt
.
IsDefault
&&
i
<
refKindsOpt
.
Length
)
{
// if we have an explicit refKind for the given argument, use that
refKind
=
refKindsOpt
[
i
];
}
else
if
(
i
<
parameters
.
Length
)
{
// otherwise check the parameter
refKind
=
parameters
[
i
].
RefKind
;
}
else
{
// vararg case
Debug
.
Assert
(
arguments
[
i
].
Kind
==
BoundKind
.
ArgListOperator
);
refKind
=
RefKind
.
None
;
}
EmitArgument
(
arguments
[
i
],
refKind
);
}
}
...
...
@@ -1068,7 +1087,7 @@ private bool FieldLoadPrefersRef(BoundExpression receiver)
}
// can we take address at all?
if
(!
HasHome
(
receiver
,
needWriteable
:
false
))
if
(!
HasHome
(
receiver
,
AddressKind
.
ReadOnly
))
{
return
false
;
}
...
...
@@ -1491,13 +1510,8 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
if
(
method
.
IsMetadataVirtual
())
{
// NB: all methods that a struct could inherit from bases are non-mutating
// we are passing here "Writeable" just to keep verifier happy
// we can pass here "ReadOnly" and avoid unnecessary copy
var
addressKind
=
EnablePEVerifyCompat
()
?
AddressKind
.
Writeable
:
AddressKind
.
ReadOnly
;
tempOpt
=
EmitReceiverRef
(
receiver
,
addressKind
);
// treat receiver as ReadOnly
tempOpt
=
EmitReceiverRef
(
receiver
,
AddressKind
.
ReadOnly
);
callKind
=
CallKind
.
ConstrainedCallVirt
;
}
else
...
...
@@ -1569,7 +1583,7 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
}
}
EmitArguments
(
arguments
,
method
.
Parameters
);
EmitArguments
(
arguments
,
method
.
Parameters
,
call
.
ArgumentRefKindsOpt
);
int
stackBehavior
=
GetCallStackBehavior
(
call
);
switch
(
callKind
)
{
...
...
@@ -1861,7 +1875,7 @@ private void EmitObjectCreationExpression(BoundObjectCreationExpression expressi
}
else
{
EmitArguments
(
expression
.
Arguments
,
constructor
.
Parameters
);
EmitArguments
(
expression
.
Arguments
,
constructor
.
Parameters
,
expression
.
ArgumentRefKindsOpt
);
var
stackAdjustment
=
GetObjCreationStackBehavior
(
expression
);
_builder
.
EmitOpCode
(
ILOpCode
.
Newobj
,
stackAdjustment
);
...
...
@@ -2033,7 +2047,7 @@ private bool TryEmitAssignmentInPlace(BoundAssignmentOperator assignmentOperator
private
bool
SafeToGetWriteableReference
(
BoundExpression
left
)
{
if
(!
HasHome
(
left
,
needWriteable
:
tru
e
))
if
(!
HasHome
(
left
,
AddressKind
.
Writeabl
e
))
{
return
false
;
}
...
...
@@ -2079,7 +2093,7 @@ private void InPlaceCtorCall(BoundExpression target, BoundObjectCreationExpressi
Debug
.
Assert
(
temp
==
null
,
"in-place ctor target should not create temps"
);
var
constructor
=
objCreation
.
Constructor
;
EmitArguments
(
objCreation
.
Arguments
,
constructor
.
Parameters
);
EmitArguments
(
objCreation
.
Arguments
,
constructor
.
Parameters
,
objCreation
.
ArgumentRefKindsOpt
);
// -2 to adjust for consumed target address and not produced value.
var
stackAdjustment
=
GetObjCreationStackBehavior
(
objCreation
)
-
2
;
_builder
.
EmitOpCode
(
ILOpCode
.
Call
,
stackAdjustment
);
...
...
@@ -2335,10 +2349,11 @@ private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
else
{
int
exprTempsBefore
=
_expressionTemps
?.
Count
??
0
;
var
local
=
((
BoundLocal
)
assignmentOperator
.
Left
).
LocalSymbol
;
// NOTE: passing "ReadOnly
" here. Assuming we do not have compile errors,
//
We should not get an address of a copy, even if the RHS is readonly
LocalDefinition
temp
=
EmitAddress
(
assignmentOperator
.
Right
,
AddressKind
.
ReadOnly
);
// NOTE: passing "ReadOnly
Strict" here.
//
we should not get an address of a copy if at all possible
LocalDefinition
temp
=
EmitAddress
(
assignmentOperator
.
Right
,
local
.
RefKind
==
RefKind
.
RefReadOnly
?
AddressKind
.
ReadOnlyStrict
:
AddressKind
.
Writeable
);
// Generally taking a ref for the purpose of ref assignment should not be done on homeless values
// however, there are very rare cases when we need to get a ref off a temp in synthetic code.
...
...
@@ -2346,7 +2361,7 @@ private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
AddExpressionTemp
(
temp
);
// are we, by the way, ref-assigning to something that lives longer than encompassing expression?
if
(
((
BoundLocal
)
assignmentOperator
.
Left
).
LocalSymbo
l
.
SynthesizedKind
.
IsLongLived
())
if
(
loca
l
.
SynthesizedKind
.
IsLongLived
())
{
var
exprTempsAfter
=
_expressionTemps
?.
Count
??
0
;
...
...
src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
浏览文件 @
ab63bff3
...
...
@@ -724,7 +724,10 @@ private void EmitReturnStatement(BoundReturnStatement boundReturnStatement)
}
else
{
this
.
EmitAddress
(
expressionOpt
,
this
.
_method
.
RefKind
==
RefKind
.
RefReadOnly
?
AddressKind
.
ReadOnly
:
AddressKind
.
Writeable
);
// NOTE: passing "ReadOnlyStrict" here.
// we should never return an address of a copy
var
unexpectedTemp
=
this
.
EmitAddress
(
expressionOpt
,
this
.
_method
.
RefKind
==
RefKind
.
RefReadOnly
?
AddressKind
.
ReadOnlyStrict
:
AddressKind
.
Writeable
);
Debug
.
Assert
(
unexpectedTemp
==
null
,
"ref-returning a temp?"
);
}
if
(
ShouldUseIndirectReturn
())
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
浏览文件 @
ab63bff3
...
...
@@ -718,7 +718,6 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
if
(
IsSafeForReordering
(
argument
,
argRefKind
))
{
arguments
[
p
]
=
argument
;
refKinds
[
p
]
=
argRefKind
;
}
else
{
...
...
@@ -727,6 +726,7 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
storesToTemps
.
Add
(
assignment
);
arguments
[
p
]
=
temp
;
}
refKinds
[
p
]
=
argRefKind
;
}
}
...
...
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs
浏览文件 @
ab63bff3
...
...
@@ -254,8 +254,10 @@ public override BoundNode VisitFixedLocalCollectionInitializer(BoundFixedLocalCo
pinnedTemp
=
factory
.
SynthesizedLocal
(
initializerType
,
syntax
:
declarator
,
isPinned
:
true
,
refKind
:
RefKind
.
Ref
,
// different from the array and string cases
isPinned
:
true
,
//NOTE: different from the array and string cases
// RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways)
refKind
:
RefKind
.
RefReadOnly
,
kind
:
SynthesizedLocalKind
.
FixedReference
);
// NOTE: we pin the reference, not the pointer.
...
...
src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
浏览文件 @
ab63bff3
...
...
@@ -1269,7 +1269,7 @@ internal static BoundExpression NullOrDefault(TypeSymbol typeSymbol, SyntaxNode
#endif
)
{
if
(
refKind
==
RefKind
.
Out
||
refKind
==
RefKind
.
In
)
if
(
refKind
==
RefKind
.
Out
)
{
refKind
=
RefKind
.
Ref
;
}
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
浏览文件 @
ab63bff3
...
...
@@ -233,6 +233,64 @@ .locals init (int V_0)
}
[
Fact
]
[
CompilerTrait
(
CompilerFeature
.
PEVerifyCompat
)]
public
void
InParamPassRoField1
()
{
var
text
=
@"
class Program
{
public static readonly int F = 42;
public static void Main()
{
System.Console.WriteLine(M(in F));
}
static ref readonly int M(in int x)
{
return ref x;
}
}
"
;
var
comp
=
CompileAndVerify
(
text
,
parseOptions
:
TestOptions
.
Regular
,
verify
:
false
,
expectedOutput
:
"42"
);
comp
.
VerifyIL
(
"Program.Main()"
,
@"
{
// Code size 17 (0x11)
.maxstack 1
IL_0000: ldsflda ""int Program.F""
IL_0005: call ""ref readonly int Program.M(in int)""
IL_000a: ldind.i4
IL_000b: call ""void System.Console.WriteLine(int)""
IL_0010: ret
}"
);
comp
.
VerifyIL
(
"Program.M(in int)"
,
@"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}"
);
comp
=
CompileAndVerify
(
text
,
verify
:
false
,
expectedOutput
:
"42"
,
parseOptions
:
TestOptions
.
Regular
.
WithPEVerifyCompatFeature
());
comp
.
VerifyIL
(
"Program.Main()"
,
@"
{
// Code size 17 (0x11)
.maxstack 1
IL_0000: ldsflda ""int Program.F""
IL_0005: call ""ref readonly int Program.M(in int)""
IL_000a: ldind.i4
IL_000b: call ""void System.Console.WriteLine(int)""
IL_0010: ret
}"
);
}
[
Fact
]
public
void
InParamPassRoParamReturn
()
{
...
...
@@ -798,6 +856,143 @@ public static void M1(in int arg1, in int arg2, in int arg3)
);
}
[
Fact
]
public
void
ReadonlyParamAsyncSpillInRoField
()
{
var
text
=
@"
using System.Threading.Tasks;
class Program
{
public static readonly int F = 5;
static void Main(string[] args)
{
Test().Wait();
}
public static async Task Test()
{
int local = 1;
M1(in F, await GetT(2), 3);
}
public static async Task<T> GetT<T>(T val)
{
await Task.Yield();
MutateReadonlyField();
return val;
}
private static unsafe void MutateReadonlyField()
{
fixed(int* ptr = &F)
{
*ptr = 42;
}
}
public static void M1(in int arg1, in int arg2, in int arg3)
{
System.Console.WriteLine(arg1 + arg2 + arg3);
}
}
"
;
var
comp
=
CreateCompilationWithMscorlib46
(
text
,
new
[]
{
ValueTupleRef
,
SystemRuntimeFacadeRef
},
options
:
TestOptions
.
UnsafeReleaseExe
);
var
result
=
CompileAndVerify
(
comp
,
verify
:
false
,
expectedOutput
:
@"47"
);
var
expectedIL
=
@"
{
// Code size 162 (0xa2)
.maxstack 3
.locals init (int V_0,
int V_1,
System.Runtime.CompilerServices.TaskAwaiter<int> V_2,
int V_3,
System.Exception V_4)
IL_0000: ldarg.0
IL_0001: ldfld ""int Program.<Test>d__2.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_003f
IL_000a: ldc.i4.2
IL_000b: call ""System.Threading.Tasks.Task<int> Program.GetT<int>(int)""
IL_0010: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0015: stloc.2
IL_0016: ldloca.s V_2
IL_0018: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_001d: brtrue.s IL_005b
IL_001f: ldarg.0
IL_0020: ldc.i4.0
IL_0021: dup
IL_0022: stloc.0
IL_0023: stfld ""int Program.<Test>d__2.<>1__state""
IL_0028: ldarg.0
IL_0029: ldloc.2
IL_002a: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1""
IL_002f: ldarg.0
IL_0030: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder""
IL_0035: ldloca.s V_2
IL_0037: ldarg.0
IL_0038: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<Test>d__2>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<Test>d__2)""
IL_003d: leave.s IL_00a1
IL_003f: ldarg.0
IL_0040: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1""
IL_0045: stloc.2
IL_0046: ldarg.0
IL_0047: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1""
IL_004c: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0052: ldarg.0
IL_0053: ldc.i4.m1
IL_0054: dup
IL_0055: stloc.0
IL_0056: stfld ""int Program.<Test>d__2.<>1__state""
IL_005b: ldloca.s V_2
IL_005d: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_0062: stloc.1
IL_0063: ldsflda ""int Program.F""
IL_0068: ldloca.s V_1
IL_006a: ldc.i4.3
IL_006b: stloc.3
IL_006c: ldloca.s V_3
IL_006e: call ""void Program.M1(in int, in int, in int)""
IL_0073: leave.s IL_008e
}
catch System.Exception
{
IL_0075: stloc.s V_4
IL_0077: ldarg.0
IL_0078: ldc.i4.s -2
IL_007a: stfld ""int Program.<Test>d__2.<>1__state""
IL_007f: ldarg.0
IL_0080: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder""
IL_0085: ldloc.s V_4
IL_0087: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_008c: leave.s IL_00a1
}
IL_008e: ldarg.0
IL_008f: ldc.i4.s -2
IL_0091: stfld ""int Program.<Test>d__2.<>1__state""
IL_0096: ldarg.0
IL_0097: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder""
IL_009c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_00a1: ret
}
"
;
result
.
VerifyIL
(
"Program.<Test>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()"
,
expectedIL
);
comp
=
CreateCompilationWithMscorlib46
(
text
,
new
[]
{
ValueTupleRef
,
SystemRuntimeFacadeRef
},
options
:
TestOptions
.
UnsafeReleaseExe
,
parseOptions
:
TestOptions
.
Regular
.
WithPEVerifyCompatFeature
());
result
=
CompileAndVerify
(
comp
,
verify
:
false
,
expectedOutput
:
@"47"
);
result
.
VerifyIL
(
"Program.<Test>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()"
,
expectedIL
);
}
[
Fact
]
public
void
InParamAsyncSpill2
()
{
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
浏览文件 @
ab63bff3
...
...
@@ -54,35 +54,30 @@ class C
}
}"
;
var
comp
=
CompileAndVerify
(
source
,
parseOptions
:
TestOptions
.
Regular
.
WithPEVerifyCompatFeature
());
// WithPEVerifyCompatFeature should not cause us to get a ref of a temp in ref assignments
var
comp
=
CompileAndVerify
(
source
,
parseOptions
:
TestOptions
.
Regular
.
WithPEVerifyCompatFeature
(),
verify
:
false
);
comp
.
VerifyIL
(
"C.M"
,
@"
{
// Code size
65 (0x41
)
// Code size
59 (0x3b
)
.maxstack 2
.locals init (S V_0,
S V_1,
S2 V_2)
.locals init (S V_0)
IL_0000: ldsflda ""S C.s1""
IL_0005: dup
IL_0006: ldobj ""S""
IL_000b: stloc.0
IL_000c: ldloca.s V_0
IL_000e: call ""void S.AddOne()""
IL_0013: ldsfld ""S C.s2""
IL_0018: stloc.0
IL_0019: ldloca.s V_0
IL_001b: ldobj ""S""
IL_0020: stloc.1
IL_0021: ldloca.s V_1
IL_0023: call ""void S.AddOne()""
IL_0028: ldsflda ""S2 C.s3""
IL_002d: call ""void S2.AddOne()""
IL_0032: ldarg.0
IL_0033: ldfld ""S2 C.s4""
IL_0038: stloc.2
IL_0039: ldloca.s V_2
IL_003b: call ""void S2.AddOne()""
IL_0040: ret
IL_0013: ldsflda ""S C.s2""
IL_0018: ldobj ""S""
IL_001d: stloc.0
IL_001e: ldloca.s V_0
IL_0020: call ""void S.AddOne()""
IL_0025: ldsflda ""S2 C.s3""
IL_002a: call ""void S2.AddOne()""
IL_002f: ldarg.0
IL_0030: ldflda ""S2 C.s4""
IL_0035: call ""void S2.AddOne()""
IL_003a: ret
}"
);
comp
=
CompileAndVerify
(
source
,
verify
:
false
);
...
...
@@ -864,47 +859,36 @@ .maxstack 1
IL_0033: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0038: ret
}"
);
comp
=
CompileAndVerify
(
text
,
new
[]
{
ValueTupleRef
,
SystemRuntimeFacadeRef
},
parseOptions
:
TestOptions
.
Regular
.
WithPEVerifyCompatFeature
(),
verify
:
false
);
// WithPEVerifyCompatFeature should not cause us to get a ref of a temp in ref returns
comp
=
CompileAndVerify
(
text
,
new
[]
{
ValueTupleRef
,
SystemRuntimeFacadeRef
},
parseOptions
:
TestOptions
.
Regular
.
WithPEVerifyCompatFeature
(),
verify
:
false
);
comp
.
VerifyIL
(
"Program.Test"
,
@"
{
// Code size
70 (0x46
)
// Code size
57 (0x39
)
.maxstack 1
.locals init (bool V_0, //b
int V_1,
System.ValueTuple<int, int> V_2,
int V_3,
System.ValueTuple<int, int> V_4)
.locals init (bool V_0) //b
IL_0000: ldc.i4.1
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: brfalse.s IL_00
20
IL_0003: brfalse.s IL_00
1a
IL_0005: ldloc.0
IL_0006: brfalse.s IL_00
12
IL_0006: brfalse.s IL_00
0f
IL_0008: ldarg.0
IL_0009: ldfld ""int Program.F""
IL_000e: stloc.1
IL_000f: ldloca.s V_1
IL_0011: ret
IL_0012: ldsfld ""(int Alice, int Bob) Program.F1""
IL_0017: stloc.2
IL_0018: ldloca.s V_2
IL_001a: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_001f: ret
IL_0020: ldloc.0
IL_0021: brfalse.s IL_0032
IL_0023: ldarg.0
IL_0024: ldfld ""Program.S Program.S1""
IL_0029: ldfld ""int Program.S.F""
IL_002e: stloc.3
IL_002f: ldloca.s V_3
IL_0031: ret
IL_0032: ldsfld ""Program.S Program.S2""
IL_0037: ldfld ""(int Alice, int Bob) Program.S.F1""
IL_003c: stloc.s V_4
IL_003e: ldloca.s V_4
IL_0040: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0045: ret
IL_0009: ldflda ""int Program.F""
IL_000e: ret
IL_000f: ldsflda ""(int Alice, int Bob) Program.F1""
IL_0014: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0019: ret
IL_001a: ldloc.0
IL_001b: brfalse.s IL_0029
IL_001d: ldarg.0
IL_001e: ldflda ""Program.S Program.S1""
IL_0023: ldflda ""int Program.S.F""
IL_0028: ret
IL_0029: ldsflda ""Program.S Program.S2""
IL_002e: ldflda ""(int Alice, int Bob) Program.S.F1""
IL_0033: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_0038: ret
}"
);
}
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs
浏览文件 @
ab63bff3
...
...
@@ -12025,7 +12025,7 @@ public MyManagedStruct(int x)
n.n.num = x;
}
}"
;
var
comp
=
CompileAndVerify
(
source
,
expectedOutput
:
@"42"
,
verify
:
false
);
var
comp
=
CompileAndVerify
(
source
,
expectedOutput
:
@"42"
,
parseOptions
:
TestOptions
.
Regular7_2
,
verify
:
false
);
comp
.
VerifyIL
(
"Program.Main"
,
@"
...
...
@@ -12088,6 +12088,38 @@ .locals init (MyManagedStruct V_0)
IL_0046: call ""void System.Console.WriteLine(int)""
IL_004b: ret
}
"
);
comp
=
CompileAndVerify
(
source
,
expectedOutput
:
@"42"
,
verify
:
true
,
parseOptions
:
TestOptions
.
Regular7_1
);
comp
.
VerifyIL
(
"Program.Main"
,
@"
{
// Code size 76 (0x4c)
.maxstack 3
.locals init (MyManagedStruct V_0)
IL_0000: newobj ""cls1..ctor()""
IL_0005: dup
IL_0006: ldfld ""MyManagedStruct cls1.y""
IL_000b: stloc.0
IL_000c: ldloca.s V_0
IL_000e: ldc.i4.s 123
IL_0010: call ""void MyManagedStruct.mutate(int)""
IL_0015: dup
IL_0016: ldfld ""MyManagedStruct cls1.y""
IL_001b: stloc.0
IL_001c: ldloca.s V_0
IL_001e: ldflda ""MyManagedStruct.Nested MyManagedStruct.n""
IL_0023: ldflda ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
IL_0028: ldc.i4 0x1c8
IL_002d: call ""void MyManagedStruct.Nested.Nested1.mutate(int)""
IL_0032: ldfld ""MyManagedStruct cls1.y""
IL_0037: ldfld ""MyManagedStruct.Nested MyManagedStruct.n""
IL_003c: ldfld ""MyManagedStruct.Nested.Nested1 MyManagedStruct.Nested.n""
IL_0041: ldfld ""int MyManagedStruct.Nested.Nested1.num""
IL_0046: call ""void System.Console.WriteLine(int)""
IL_004b: ret
}
"
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录