Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
3fe5a03f
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,发现更多精彩内容 >>
提交
3fe5a03f
编写于
3月 14, 2017
作者:
V
vsadov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refs of readonly fields, parameters, returns
上级
e8d146c8
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
221 addition
and
104 deletion
+221
-104
src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
+74
-44
src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
+42
-24
src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
+6
-4
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
...lers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
+80
-0
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
...CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
+19
-32
未找到文件。
src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
浏览文件 @
3fe5a03f
...
...
@@ -17,8 +17,13 @@ private enum AddressKind
// reference may be written to
Writeable
,
// reference itself will not be written to, but may be used to modify fields.
ReadOnly
// reference itself will not be written to, but may be used for call, callvirt.
// for all purposes it the same as Writeable, except when fetching an address of an array element
// where it results in a ".readonly" prefix to deal with array covariance.
Constrained
,
// reference itself will not be written to, nor it will be used to modify fields.
Readonly
,
}
/// <summary>
...
...
@@ -58,7 +63,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
break
;
case
BoundKind
.
FieldAccess
:
return
EmitFieldAddress
((
BoundFieldAccess
)
expression
);
return
EmitFieldAddress
((
BoundFieldAccess
)
expression
,
addressKind
);
case
BoundKind
.
ArrayAccess
:
//arrays are covariant, but elements can be written to.
...
...
@@ -95,13 +100,16 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
case
BoundKind
.
Call
:
var
call
=
(
BoundCall
)
expression
;
if
(
call
.
Method
.
RefKind
==
RefKind
.
None
)
var
methodRefKind
=
call
.
Method
.
RefKind
;
if
(
methodRefKind
==
RefKind
.
Ref
||
(
addressKind
==
AddressKind
.
Readonly
&&
methodRefKind
==
RefKind
.
RefReadOnly
))
{
goto
default
;
EmitCallExpression
(
call
,
UseKind
.
UsedAsAddress
);
break
;
}
EmitCallExpression
(
call
,
UseKind
.
UsedAsAddress
);
break
;
goto
default
;
case
BoundKind
.
ConditionalOperator
:
var
conditional
=
(
BoundConditionalOperator
)
expression
;
...
...
@@ -110,7 +118,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
goto
default
;
}
EmitConditionalOperatorAddress
(
conditional
);
EmitConditionalOperatorAddress
(
conditional
,
addressKind
);
break
;
case
BoundKind
.
AssignmentOperator
:
...
...
@@ -123,7 +131,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
throw
ExceptionUtilities
.
UnexpectedValue
(
assignment
.
RefKind
);
default
:
Debug
.
Assert
(!
HasHome
(
expression
));
Debug
.
Assert
(!
HasHome
(
expression
,
addressKind
!=
AddressKind
.
Readonly
));
return
EmitAddressOfTempClone
(
expression
);
}
...
...
@@ -143,7 +151,7 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
/// push x
/// DONE:
/// </remarks>
private
void
EmitConditionalOperatorAddress
(
BoundConditionalOperator
expr
)
private
void
EmitConditionalOperatorAddress
(
BoundConditionalOperator
expr
,
AddressKind
addressKind
)
{
Debug
.
Assert
(
expr
.
ConstantValue
==
null
,
"Constant value should have been emitted directly"
);
...
...
@@ -151,7 +159,7 @@ private void EmitConditionalOperatorAddress(BoundConditionalOperator expr)
object
doneLabel
=
new
object
();
EmitCondBranch
(
expr
.
Condition
,
ref
consequenceLabel
,
sense
:
true
);
var
temp
=
EmitAddress
(
expr
.
Alternative
,
AddressKind
.
Writeable
);
var
temp
=
EmitAddress
(
expr
.
Alternative
,
addressKind
);
Debug
.
Assert
(
temp
==
null
);
_builder
.
EmitBranch
(
ILOpCode
.
Br
,
doneLabel
);
...
...
@@ -160,7 +168,7 @@ private void EmitConditionalOperatorAddress(BoundConditionalOperator expr)
_builder
.
AdjustStack
(-
1
);
_builder
.
MarkLabel
(
consequenceLabel
);
EmitAddress
(
expr
.
Consequence
,
AddressKind
.
Writeable
);
EmitAddress
(
expr
.
Consequence
,
addressKind
);
_builder
.
MarkLabel
(
doneLabel
);
}
...
...
@@ -180,13 +188,14 @@ private void EmitComplexConditionalReceiverAddress(BoundComplexConditionalReceiv
EmitBox
(
receiverType
,
expression
.
Syntax
);
_builder
.
EmitBranch
(
ILOpCode
.
Brtrue
,
whenValueTypeLabel
);
var
receiverTemp
=
EmitAddress
(
expression
.
ReferenceTypeReceiver
,
addressKind
:
AddressKind
.
ReadO
nly
);
var
receiverTemp
=
EmitAddress
(
expression
.
ReferenceTypeReceiver
,
AddressKind
.
Reado
nly
);
Debug
.
Assert
(
receiverTemp
==
null
);
_builder
.
EmitBranch
(
ILOpCode
.
Br
,
doneLabel
);
_builder
.
AdjustStack
(-
1
);
_builder
.
MarkLabel
(
whenValueTypeLabel
);
EmitReceiverRef
(
expression
.
ValueTypeReceiver
,
isAccessConstrained
:
true
);
// we will not write through this receiver, but it could be a target of mutating calls
EmitReceiverRef
(
expression
.
ValueTypeReceiver
,
AddressKind
.
Constrained
);
_builder
.
MarkLabel
(
doneLabel
);
}
...
...
@@ -325,11 +334,10 @@ 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
)
private
bool
HasHome
(
BoundExpression
expression
,
bool
needWriteable
)
{
switch
(
expression
.
Kind
)
{
case
BoundKind
.
Parameter
:
case
BoundKind
.
ArrayAccess
:
case
BoundKind
.
ThisReference
:
case
BoundKind
.
BaseReference
:
...
...
@@ -337,61 +345,83 @@ private bool HasHome(BoundExpression expression)
case
BoundKind
.
RefValueOperator
:
return
true
;
case
BoundKind
.
Parameter
:
return
!
needWriteable
||
((
BoundParameter
)
expression
).
ParameterSymbol
.
RefKind
!=
RefKind
.
RefReadOnly
;
case
BoundKind
.
Local
:
// locals have home unless they are byval stack locals
var
local
=
((
BoundLocal
)
expression
).
LocalSymbol
;
return
!
IsStackLocal
(
local
)
||
local
.
RefKind
!=
RefKind
.
None
;
case
BoundKind
.
Call
:
var
method
=
((
BoundCall
)
expression
).
Method
;
return
method
.
RefKind
!=
RefKind
.
None
;
var
methodRefKind
=
((
BoundCall
)
expression
).
Method
.
RefKind
;
return
methodRefKind
==
RefKind
.
Ref
||
(!
needWriteable
&&
methodRefKind
==
RefKind
.
RefReadOnly
);
case
BoundKind
.
Dup
:
//PROTOTYPE(readonlyRefs): makes sure readonly variables are not duped and written to
return
((
BoundDup
)
expression
).
RefKind
!=
RefKind
.
None
;
case
BoundKind
.
FieldAccess
:
return
HasHome
((
BoundFieldAccess
)
expression
);
return
HasHome
((
BoundFieldAccess
)
expression
,
needWriteable
);
case
BoundKind
.
Sequence
:
return
HasHome
(((
BoundSequence
)
expression
).
Value
);
return
HasHome
(((
BoundSequence
)
expression
).
Value
,
needWriteable
);
case
BoundKind
.
AssignmentOperator
:
return
((
BoundAssignmentOperator
)
expression
).
RefKind
!=
RefKind
.
None
;
case
BoundKind
.
ComplexConditionalReceiver
:
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ValueTypeReceiver
));
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ReferenceTypeReceiver
));
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ValueTypeReceiver
,
needWriteable
));
Debug
.
Assert
(
HasHome
(((
BoundComplexConditionalReceiver
)
expression
).
ReferenceTypeReceiver
,
needWriteable
));
goto
case
BoundKind
.
ConditionalReceiver
;
case
BoundKind
.
ConditionalReceiver
:
//PROTOTYPE(readonlyRefs): are these always writeable? Test coverage?
return
true
;
case
BoundKind
.
ConditionalOperator
:
//PROTOTYPE(readonlyRefs): Test coverage for in-place assignment/init.
return
((
BoundConditionalOperator
)
expression
).
IsByRef
;
default
:
return
false
;
}
}
/// <summary>
/// Special HasHome for fields. Fields have homes when they are writable.
/// Special HasHome for fields.
/// 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
)
private
bool
HasHome
(
BoundFieldAccess
fieldAccess
,
bool
needWriteable
)
{
// Some field accesses must be values; values do not have homes.
if
(
fieldAccess
.
IsByValue
)
FieldSymbol
field
=
fieldAccess
.
FieldSymbol
;
// const fields are literal values with no homes
if
(
field
.
IsConst
)
{
//PROTOTYPE(readonlyRefs): does this actually happen?
return
false
;
}
FieldSymbol
field
=
fieldAccess
.
FieldSymbol
;
if
(!
needWriteable
)
{
return
true
;
}
//
const fields are literal values with no homes
if
(
field
.
IsConst
)
//
Some field accesses must be values; values do not have homes.
if
(
field
Access
.
IsByValue
)
{
return
false
;
}
if
(!
field
.
IsReadOnly
)
{
//PROTOTYPE(readonlyRefs): should we dig through struct receivers?
// roField.a.b.c.d.Method() // roField is readonly, all structs
// is it cheaper to copy "d" than "roField", but getting rw ref of roField could upset verifier
return
true
;
}
...
...
@@ -427,7 +457,7 @@ private void EmitArrayElementAddress(BoundArrayAccess arrayAccess, AddressKind a
EmitExpression
(
arrayAccess
.
Expression
,
used
:
true
);
EmitArrayIndices
(
arrayAccess
.
Indices
);
if
(
addressKind
==
AddressKind
.
ReadOnly
)
if
(
addressKind
==
AddressKind
.
Constrained
)
{
Debug
.
Assert
(
arrayAccess
.
Type
.
TypeKind
==
TypeKind
.
TypeParameter
,
".readonly is only needed when element type is a type param"
);
...
...
@@ -451,11 +481,11 @@ private void EmitArrayElementAddress(BoundArrayAccess arrayAccess, AddressKind a
/// <summary>
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private
LocalDefinition
EmitFieldAddress
(
BoundFieldAccess
fieldAccess
)
private
LocalDefinition
EmitFieldAddress
(
BoundFieldAccess
fieldAccess
,
AddressKind
addressKind
)
{
FieldSymbol
field
=
fieldAccess
.
FieldSymbol
;
if
(!
HasHome
(
fieldAccess
))
if
(!
HasHome
(
fieldAccess
,
addressKind
!=
AddressKind
.
Readonly
))
{
// accessing a field that is not writable (const or readonly)
return
EmitAddressOfTempClone
(
fieldAccess
);
...
...
@@ -467,7 +497,7 @@ private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess)
}
else
{
return
EmitInstanceFieldAddress
(
fieldAccess
);
return
EmitInstanceFieldAddress
(
fieldAccess
,
isReadonly
:
addressKind
==
AddressKind
.
Readonly
);
}
}
...
...
@@ -491,16 +521,16 @@ private void EmitParameterAddress(BoundParameter parameter)
}
/// <summary>
/// Emits receiver in a form that allows member accesses ( O or & ). For verifiably
/// reference types it is the actual reference. For generic types it is a address of the
/// receiver with readonly intent. For the value types it is an address of the receiver.
/// Emits receiver in a form that allows member accesses ( O or & ).
/// For verifiably reference types it is the actual reference.
/// For the value types it is an address of the receiver.
/// For generic types it is either a boxed receiver or the address of the receiver with readonly intent.
///
/// isAccessConstrained indicates that receiver is a target of a constrained callvirt
/// in such case it is unnecessary to box a receiver that is typed to a type parameter
/// addressKind - kind of address that is needed in case if receiver is not a reference type.
///
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private
LocalDefinition
EmitReceiverRef
(
BoundExpression
receiver
,
bool
isAccessConstrained
=
false
)
private
LocalDefinition
EmitReceiverRef
(
BoundExpression
receiver
,
AddressKind
addressKind
)
{
var
receiverType
=
receiver
.
Type
;
if
(
receiverType
.
IsVerifierReference
())
...
...
@@ -518,9 +548,9 @@ private LocalDefinition EmitReceiverRef(BoundExpression receiver, bool isAccessC
//via the generic parameter unless it is first boxed (see Partition III) or
//the callvirt instruction is prefixed with the constrained. prefix instruction
//(see Partition III). end note]
if
(
isAccess
Constrained
)
if
(
addressKind
==
AddressKind
.
Constrained
)
{
return
EmitAddress
(
receiver
,
AddressKind
.
ReadOnly
);
return
EmitAddress
(
receiver
,
addressKind
);
}
else
{
...
...
@@ -535,17 +565,17 @@ private LocalDefinition EmitReceiverRef(BoundExpression receiver, bool isAccessC
}
Debug
.
Assert
(
receiverType
.
IsVerifierValue
());
return
EmitAddress
(
receiver
,
AddressKind
.
Writeable
);
return
EmitAddress
(
receiver
,
addressKind
);
}
/// <summary>
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private
LocalDefinition
EmitInstanceFieldAddress
(
BoundFieldAccess
fieldAccess
)
private
LocalDefinition
EmitInstanceFieldAddress
(
BoundFieldAccess
fieldAccess
,
bool
isReadonly
)
{
var
field
=
fieldAccess
.
FieldSymbol
;
var
tempOpt
=
EmitReceiverRef
(
fieldAccess
.
ReceiverOpt
);
var
tempOpt
=
EmitReceiverRef
(
fieldAccess
.
ReceiverOpt
,
isReadonly
?
AddressKind
.
Readonly
:
AddressKind
.
Writeable
);
_builder
.
EmitOpCode
(
ILOpCode
.
Ldflda
);
EmitSymbolToken
(
field
,
fieldAccess
.
Syntax
);
...
...
src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
浏览文件 @
3fe5a03f
...
...
@@ -359,8 +359,10 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
var
receiverConstant
=
receiver
.
ConstantValue
;
if
(
receiverConstant
!=
null
)
{
// const but not default
receiverTemp
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
!
receiverType
.
IsReferenceType
);
// const but not default, must be a reference type
Debug
.
Assert
(
receiverType
.
IsVerifierReference
());
// receiver is a reference type, so addresskind does not matter, but we do not intend to write.
receiverTemp
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Readonly
);
EmitExpression
(
expression
.
WhenNotNull
,
used
);
if
(
receiverTemp
!=
null
)
{
...
...
@@ -374,7 +376,7 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
object
doneLabel
=
new
object
();
LocalDefinition
cloneTemp
=
null
;
var
unconstrainedReceiver
=
!
receiverType
.
IsReferenceType
&&
!
receiverType
.
IsValueType
;
var
notConstrained
=
!
receiverType
.
IsReferenceType
&&
!
receiverType
.
IsValueType
;
// we need a copy if we deal with nonlocal value (to capture the value)
// or if we have a ref-constrained T (to do box just once)
...
...
@@ -382,14 +384,17 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
// or if we have default(T) (to do box just once)
var
nullCheckOnCopy
=
LocalRewriter
.
CanChangeValueBetweenReads
(
receiver
,
localsMayBeAssignedOrCaptured
:
false
)
||
(
receiverType
.
IsReferenceType
&&
receiverType
.
TypeKind
==
TypeKind
.
TypeParameter
)
||
(
receiver
.
Kind
==
BoundKind
.
Local
&&
IsStackLocal
(((
BoundLocal
)
receiver
).
LocalSymbol
));
(
receiver
.
Kind
==
BoundKind
.
Local
&&
IsStackLocal
(((
BoundLocal
)
receiver
).
LocalSymbol
))
||
(
receiver
.
IsDefaultValue
()
&&
notConstrained
);
// ===== RECEIVER
if
(
nullCheckOnCopy
)
{
receiverTemp
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
unconstrainedReceiver
);
if
(
unconstrainedReceiver
)
if
(
notConstrained
)
{
// if T happens to be a value type, it could be a target of mutating calls.
receiverTemp
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Constrained
);
// unconstrained case needs to handle case where T is actually a struct.
// such values are never nulls
// we will emit a check for such case, but the check is really a JIT-time
...
...
@@ -419,13 +424,18 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
}
else
{
//PROTOTYPE(readonlyRefs): this does not need to be writeable
// we may call "HasValue" on this, but it is not mutating
receiverTemp
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Writeable
);
_builder
.
EmitOpCode
(
ILOpCode
.
Dup
);
// here we have loaded two copies of a reference { O, O } or {&nub, &nub}
}
}
else
{
receiverTemp
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
false
);
//PROTOTYPE(readonlyRefs): this does not need to be writeable
// we may call "HasValue" on this, but it is not mutating
receiverTemp
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Writeable
);
// here we have loaded just { O } or {&nub}
// we have the most trivial case where we can just reload receiver when needed again
}
...
...
@@ -490,8 +500,9 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces
if
(!
nullCheckOnCopy
)
{
Debug
.
Assert
(
receiverTemp
==
null
);
receiverTemp
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
unconstrainedReceiver
);
Debug
.
Assert
(
receiverTemp
==
null
||
receiver
.
IsDefaultValue
());
// receiver may be used as target of a struct call (if T happens to be a sruct)
receiverTemp
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Constrained
);
Debug
.
Assert
(
receiverTemp
==
null
);
}
EmitExpression
(
expression
.
WhenNotNull
,
used
);
...
...
@@ -583,8 +594,7 @@ private void EmitArgument(BoundExpression argument, RefKind refKind)
case
RefKind
.
RefReadOnly
:
//PROTOTYPE(reaadonlyRefs): leaking a temp here
//PROTOTYPE(reaadonlyRefs): readonly fields should not be cloned to temps
var
temp
=
EmitAddress
(
argument
,
AddressKind
.
Writeable
);
var
temp
=
EmitAddress
(
argument
,
AddressKind
.
Readonly
);
break
;
default
:
...
...
@@ -641,8 +651,8 @@ private void EmitDupExpression(BoundDup expression, bool used)
private
void
EmitDelegateCreationExpression
(
BoundDelegateCreationExpression
expression
,
bool
used
)
{
Debug
.
Assert
(
expression
.
Argument
?.
Kind
!=
BoundKind
.
MethodGroup
)
;
var
receiver
=
expression
.
Argument
;
var
mg
=
expression
.
Argument
as
BoundMethodGroup
;
var
receiver
=
mg
!=
null
?
mg
.
ReceiverOpt
:
expression
.
Argument
;
var
meth
=
expression
.
MethodOpt
??
receiver
.
Type
.
DelegateInvokeMethod
();
Debug
.
Assert
((
object
)
meth
!=
null
);
EmitDelegateCreation
(
expression
,
receiver
,
expression
.
IsExtensionMethod
,
meth
,
expression
.
Type
,
used
);
...
...
@@ -938,7 +948,7 @@ private LocalDefinition EmitFieldLoadReceiver(BoundExpression receiver)
// there are also cases where we must emit receiver as a reference
if
(
FieldLoadMustUseRef
(
receiver
)
||
FieldLoadPrefersRef
(
receiver
))
{
return
EmitFieldLoadReceiverAddress
(
receiver
)
?
null
:
EmitReceiverRef
(
receiver
);
return
EmitFieldLoadReceiverAddress
(
receiver
)
?
null
:
EmitReceiverRef
(
receiver
,
AddressKind
.
Readonly
);
}
EmitExpression
(
receiver
,
true
);
...
...
@@ -1013,7 +1023,9 @@ private bool FieldLoadPrefersRef(BoundExpression receiver)
}
// can we take address at all?
if
(!
HasHome
(
receiver
))
//PROTOTYPE(readonlyRefs): we only need to read, so we could pass "false" here
// but that may result in getting a ref off a readonly field, which could upset verifier
if
(!
HasHome
(
receiver
,
needWriteable
:
true
))
{
return
false
;
}
...
...
@@ -1277,7 +1289,7 @@ private bool CanUseCallOnRefTypeReceiver(BoundExpression receiver)
case
ConversionKind
.
MethodGroup
:
case
ConversionKind
.
AnonymousFunction
:
throw
ExceptionUtilities
.
UnexpectedValue
(
conversion
.
ConversionKind
)
;
return
true
;
case
ConversionKind
.
ExplicitReference
:
case
ConversionKind
.
ImplicitReference
:
...
...
@@ -1364,7 +1376,7 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
Debug
.
Assert
(
method
.
ContainingType
==
receiver
.
Type
);
Debug
.
Assert
(
receiver
.
Kind
==
BoundKind
.
ThisReference
);
tempOpt
=
EmitReceiverRef
(
receiver
);
tempOpt
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Writeable
);
_builder
.
EmitOpCode
(
ILOpCode
.
Initobj
);
// initobj <MyStruct>
EmitSymbolToken
(
method
.
ContainingType
,
call
.
Syntax
);
FreeOptTemp
(
tempOpt
);
...
...
@@ -1386,7 +1398,7 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
if
(
receiverType
.
IsVerifierReference
())
{
tempOpt
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
fals
e
);
EmitExpression
(
receiver
,
used
:
tru
e
);
// In some cases CanUseCallOnRefTypeReceiver returns true which means that
// null check is unnecessary and we can use "call"
...
...
@@ -1411,7 +1423,9 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
// calling a method defined in a value type
Debug
.
Assert
(
receiverType
==
methodContainingType
);
tempOpt
=
EmitReceiverRef
(
receiver
);
// method is defined in the struct itself and is assumed to be mutating.
//PROTOTYPE(readonlyRefs): when readonly structs are implemented, this will have to check "this"
tempOpt
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Writeable
);
callKind
=
CallKind
.
Call
;
}
else
...
...
@@ -1420,7 +1434,11 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
{
// When calling a method that is virtual in metadata on a struct receiver,
// we use a constrained virtual call. If possible, it will skip boxing.
tempOpt
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
true
);
//
//PROTOTYPE(readonlyRefs): all methods that a struct could inherit from bases are non-mutating
// we are passing here "Writeable" just to keep verifier happy
// we should pass here "Readonly" and avoid unnecessary copy
tempOpt
=
EmitReceiverRef
(
receiver
,
AddressKind
.
Writeable
);
callKind
=
CallKind
.
ConstrainedCallVirt
;
}
else
...
...
@@ -1441,7 +1459,7 @@ private void EmitCallExpression(BoundCall call, UseKind useKind)
CallKind
.
CallVirt
:
CallKind
.
ConstrainedCallVirt
;
tempOpt
=
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
callKind
==
CallKind
.
ConstrainedCallVirt
);
tempOpt
=
EmitReceiverRef
(
receiver
,
callKind
==
CallKind
.
ConstrainedCallVirt
?
AddressKind
.
Constrained
:
AddressKind
.
Writeable
);
}
}
...
...
@@ -1945,7 +1963,7 @@ private bool TryEmitAssignmentInPlace(BoundAssignmentOperator assignmentOperator
private
bool
SafeToGetWriteableReference
(
BoundExpression
left
)
{
if
(!
HasHome
(
left
))
if
(!
HasHome
(
left
,
needWriteable
:
true
))
{
return
false
;
}
...
...
@@ -2068,7 +2086,7 @@ private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator)
var
left
=
(
BoundFieldAccess
)
assignmentTarget
;
if
(!
left
.
FieldSymbol
.
IsStatic
)
{
var
temp
=
EmitReceiverRef
(
left
.
ReceiverOpt
);
var
temp
=
EmitReceiverRef
(
left
.
ReceiverOpt
,
AddressKind
.
Writeable
);
Debug
.
Assert
(
temp
==
null
,
"temp is unexpected when assigning to a field"
);
lhsUsesStack
=
true
;
}
...
...
@@ -3035,9 +3053,9 @@ private TypeSymbol StackMergeType(BoundExpression expr)
var
conversion
=
(
BoundConversion
)
expr
;
var
conversionKind
=
conversion
.
ConversionKind
;
if
(
conversionKind
.
IsImplicitConversion
()
&&
conversionKind
!=
ConversionKind
.
MethodGroup
&&
conversionKind
!=
ConversionKind
.
NullLiteral
)
{
Debug
.
Assert
(
conversionKind
!=
ConversionKind
.
MethodGroup
);
return
StackMergeType
(
conversion
.
Operand
);
}
break
;
...
...
src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
浏览文件 @
3fe5a03f
...
...
@@ -487,7 +487,8 @@ private void EmitCondBranchCore(BoundExpression condition, ref object dest, bool
object
fallThrough
=
null
;
EmitCondBranch
(
receiver
,
ref
fallThrough
,
sense
:
false
);
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
false
);
// receiver is a reference type, and we only intend to read it
EmitReceiverRef
(
receiver
,
AddressKind
.
Readonly
);
EmitCondBranch
(
ca
.
WhenNotNull
,
ref
dest
,
sense
:
true
);
if
(
fallThrough
!=
null
)
...
...
@@ -500,7 +501,8 @@ private void EmitCondBranchCore(BoundExpression condition, ref object dest, bool
// gotoif(receiver == null) labDest
// gotoif(!receiver.Access) labDest
EmitCondBranch
(
receiver
,
ref
dest
,
sense
:
false
);
EmitReceiverRef
(
receiver
,
isAccessConstrained
:
false
);
// receiver is a reference type, and we only intend to read it
EmitReceiverRef
(
receiver
,
AddressKind
.
Readonly
);
condition
=
ca
.
WhenNotNull
;
goto
oneMoreTime
;
}
...
...
@@ -716,7 +718,7 @@ private void EmitReturnStatement(BoundReturnStatement boundReturnStatement)
}
else
{
this
.
EmitAddress
(
expressionOpt
,
AddressKind
.
Writeable
);
this
.
EmitAddress
(
expressionOpt
,
this
.
_method
.
RefKind
==
RefKind
.
RefReadOnly
?
AddressKind
.
Readonly
:
AddressKind
.
Writeable
);
}
if
(
ShouldUseIndirectReturn
())
...
...
@@ -1012,7 +1014,7 @@ private void EmitCatchBlock(BoundCatchBlock catchBlock)
var
temp
=
AllocateTemp
(
exceptionSource
.
Type
,
exceptionSource
.
Syntax
);
_builder
.
EmitLocalStore
(
temp
);
var
receiverTemp
=
EmitReceiverRef
(
left
.
ReceiverOpt
);
var
receiverTemp
=
EmitReceiverRef
(
left
.
ReceiverOpt
,
AddressKind
.
Writeable
);
Debug
.
Assert
(
receiverTemp
==
null
);
_builder
.
EmitLocalLoad
(
temp
);
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
浏览文件 @
3fe5a03f
...
...
@@ -109,6 +109,86 @@ .locals init (int V_0)
IL_0010: ret
}"
);
}
[
Fact
]
public
void
InParamPassRoField
()
{
var
text
=
@"
class Program
{
public static readonly int F = 42;
public static void Main()
{
System.Console.WriteLine(M(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(ref readonly int)""
IL_000a: ldind.i4
IL_000b: call ""void System.Console.WriteLine(int)""
IL_0010: ret
}"
);
comp
.
VerifyIL
(
"Program.M(ref readonly int)"
,
@"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}"
);
}
[
Fact
]
public
void
InParamPassRoParamReturn
()
{
var
text
=
@"
class Program
{
public static readonly int F = 42;
public static void Main()
{
System.Console.WriteLine(M(F));
}
static ref readonly int M(in int x)
{
return ref M1(x);
}
static ref readonly int M1(in int x)
{
return ref x;
}
}
"
;
var
comp
=
CompileAndVerify
(
text
,
parseOptions
:
TestOptions
.
Regular
,
verify
:
false
,
expectedOutput
:
"42"
);
comp
.
VerifyIL
(
"Program.M(ref readonly int)"
,
@"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldarg.0
IL_0001: call ""ref readonly int Program.M1(ref readonly int)""
IL_0006: ret
}"
);
}
[
Fact
]
public
void
RefReturnParamAccess1
()
...
...
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs
浏览文件 @
3fe5a03f
...
...
@@ -459,46 +459,33 @@ struct S
var
comp
=
CompileAndVerify
(
text
,
new
[]
{
ValueTupleRef
,
SystemRuntimeFacadeRef
},
parseOptions
:
TestOptions
.
Regular
,
verify
:
false
);
//PROTOTYPE(readonlyRef): correct emit is NYI. We should not make copies when returning r/o fields
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
}"
);
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录