Quantcast
Channel: YouTrackReSharper (RSRP) - Bug and Issue Tracker
Viewing all articles
Browse latest Browse all 106942

RSRP-63484: CodeAnalysis doesn't flag incorrect usage of ReadOnly with struct member variables

$
0
0
Reporter Oren Novotny (onovotny) Oren Novotny (onovotny)
Created Apr 2, 2008 5:19:39 PM
Updated Apr 13, 2018 3:45:56 PM
Resolved Apr 13, 2018 3:45:56 PM
Subsystem Code Analysis
Assignee Ilya Ryzhenkov (orangy)
Priority Normal
State Fixed
Type Bug
Fix version Unidentified prior version
Affected versions No Affected versions
Fixed In Version ReSharper Undefined
VsVersion All Versions
Struct member variables should never be readonly unless they're immutable. Any struct whos internal value(s) can change after creation should never be readonly.

The reason is that there's a subtley in the C# (and possibly VB) spec whereby usage of a readonly struct in any method/property other than the type initializer is done by first making a local copy (in the IL). The result is that the mutation occurs in the class method but does not affect the member varible itself. This can, and has, led to serious but hard-to-identify bugs.

ReSharper should put either a warning (orange or blue) underneath any read-only usages of a struct where the struct's member variables can be assigned outside of the struct ctor. (I.e., if the struct's members aren't readonly, are public or whose value is changed by way of a method/property in the struct, then the struct should be considered mutable and should never be used in a readonly context).

This behavior is by-design in the C# spec. I've attached a sample project that demostrates the behavior. As you can see, if the usage isn't readonly then it works as expected. If the mutable struct is made readonly, then it's behavior is different.

According to Eric Lippert, from the C# team:

Hi Oren,

I took a look at your program. The behaviour you are seeing is both by design and in the specification.

As far as I can tell, the spec for readonly only says that the value at the address can only be assigned in the ctor.


The spec does say that, but that is certainly not the _only_ thing that it says. Let me walk you through it:

The relevant fragment of your program is:

class SomeClassUsingReadOnly
{
private readonly MutableStruct _struct = new MutableStruct(1);

public int GetNextValue()
{
 return _struct.GetNextValue()
;
}
}

Consider how the semantic analyzer deals with "_struct.GetNextValue()"

First we have to figure out what "_struct" means. Section 7.5.2 of the specification says:

"... if T is the instance type of the immediately enclosing class or struct type, if the lookup identifies an instance member, and if the reference occurs within the block of an instance constructor, an instance method, or an instance accessor, the result is the same as a member access of the form this.I."

Therefore this is equivalent to "this._struct.GetNextValue();".

How do we deal with the first dot? That is, the dot in "this._struct"

The relevant section of the specification is 7.5.4, which states that when resolving "E.I" where E is an object and I is a field...

"...if the field is readonly and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is a value, namely the value of the field I in the object referenced by E."

The important word here is that the result is the VALUE of the field, NOT the variable associated with the field.

Great. What about that second dot, as in ".GetNextValue()"? We look at section 7.4.4 to find out how to invoke E.M():

"If E is not classified as a variable, then a temporary local variable of E's type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this."

Which exactly describes the behaviour you are seeing.

This is by design because one expects that a readonly value type is readonly throughout its entire value and throughout its entire lifetime after the constructor finishes.

Is that clear?

Eric

Viewing all articles
Browse latest Browse all 106942

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>