-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Emit readonly.
for constrained array access
#70949
Conversation
7f0f874
to
8b514fe
Compare
readonly.
for constrained array access
8b514fe
to
cd954d0
Compare
IL_0043: constrained. ""T"" | ||
IL_0049: callvirt ""void IMoveable.this[int].set"" | ||
IL_004e: ret | ||
IL_0006: readonly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit warry of generating unverifiable IL. It's necessary sometimes, particularly with newer constructs that were created after verification was frozen. That doesn't seem to be the case for this.
I'm not as familiar with the .readonly
prefix and had to look it up for this review. The set of allowed instructions does not include stloc / ldloc
. Wondering if that is an ommision or not. The subsequent operations we perform after ldloc
seem to fall into the allowed set.
I'd prefer to avoid this if possible. This is the opcodes that we generate today for the method:
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ldelema !!T
IL_0008: dup
IL_0009: constrained. !!T
IL_000f: callvirt instance int32 I1::get_P()
IL_0014: ldc.i4.2
IL_0015: add
IL_0016: constrained. !!T
IL_001c: callvirt instance void I1::set_P(int32)
IL_0021: nop
IL_0022: ret
Where is the ArrayTypeMismatchException
being generated today? Is it on the ldelema
instruction because the the type of the array is Base
but the elements are Derived
?
@davidwrighton in case he has any thoughts on how to best approach this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the
ArrayTypeMismatchException
being generated today? Is it on theldelema
instruction because the the type of the array isBase
but the elements areDerived
?
Yes.
The set of allowed instructions does not include
stloc / ldloc
We are not emitting readonly.
for stloc
/ldloc
in this PR, I believe, only for ldelema
.
I'm a bit warry of generating unverifiable IL. It's necessary sometimes, particularly with newer constructs that were created after verification was frozen. That doesn't seem to be the case for this.
Yeah, ok, that sounds good. We can instead spill the array access in various ways described in #70267. I will look into that.
{ | ||
resultExpr = base.VisitArrayAccess(node)!; | ||
resultExpr = arr.Update(arr.Expression, arr.Indices, inCompoundAssignmentReceiver: true, arr.Type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we only set inCompoundAssignmentReceiver
when the element type is a type parameter? If so then I think we should rename that property such that it captures. Based on the current name I was confused looking at the changes to EmitAddress.cs
as to why more baselines didn't change to have the .readonly
prefix on them.
var previousInCompoundAssignmentReceiver = _inCompoundAssignmentReceiver; | ||
_inCompoundAssignmentReceiver |= isRegularCompoundAssignment; | ||
var loweredReceiver = VisitExpression(receiver); | ||
_inCompoundAssignmentReceiver = previousInCompoundAssignmentReceiver; | ||
return loweredReceiver; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like _inCompoundAssignmentReceiver
will be set for the duration of the VisitExpression
call. Doesn't that mean that all array accesses within the receiver will end up with the .readonly
prefix? Essentially I'm thinking about the following:
array1[(array2[0]=someValue).Index].Prop += 42;
Will the inner array2[0]
get the .readonly
prefix? If so that seems incorrect and would lead to array mismatch bugs.
It looks like we already deal with a similar situation in context of a compound assignment to an array element without emitting an unverifiable code. See usages of |
I'm investigating using |
Sorry, I am not following. It looks to me that
Similar situations are situations when an array element access is used as a receiver of a method call and it is getting spilled (i.e. its side- effects are evaluated at one point of time and the actual access is performed at a later time, quite possibly more than once). |
I would like to bring your attention to the following quote from the issue:
I am pretty sure that affected scenarios are not limited to compound assignment scenarios, while they are the most obvious/simple examples. I would scope affected scenarios to scenarios for which LocalRewriter captures receivers of method calls in a ByRef local. Those places should be easily identifiable based on the helper used for that. |
@@ -664,7 +664,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL | |||
// I t_index = index; (possibly more indices) | |||
// T value = t_array[t_index]; | |||
// t_array[t_index] = value op R; | |||
var loweredArray = VisitExpression(arrayAccess.Expression); | |||
|
|||
var loweredArray = VisitCompoundAssignmentReceiver(arrayAccess.Expression, isRegularCompoundAssignment); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
resultExpr = base.VisitArrayAccess(node)!; | ||
resultExpr = arr.Update(arr.Expression, arr.Indices, inCompoundAssignmentReceiver: true, arr.Type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that, in a general case, this assumption is incorrect. The expression that we are rewriting while _inCompoundAssignmentReceiver
is set to true could be arbitrarily complex, with any number of array access expressions used in it in any number of positions that are not receiver positions. More over, the array access can be a target of an explicit ref
taking operation, like right hand side of a ref
assignment, or a ref
argument, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify. Strictly speaking, yes, the array access is in a compound assignment receiver. However, the value of inCompoundAssignmentReceiver
is used by emit layer as an indicator that using readonly.
prefix is appropriate. Which is incorrect in a general case, even if we put the IL verification issue aside.
Oh, thanks, I didn't think of looking into this scenario. Seems that it's not behaving as I would expect though. Your test
Yes, that's true. VB checks at runtime if
Today, |
@jjonescz I think it is time we sync up offline. |
I have absolutely no idea what exactly you are doing, and, therefore, cannot comment on results that you are getting and why you are getting them. |
Fixes #70267.