The last few days I have been playing around with spec# and reflector alot. In doing so I came accross a pretty serious bug I had to fix in my dynamic proxy. There is this mostly unused but largely important item in IL called a modopt/req. For those more interested in C#/spec# and handling ... scroll down as there is further information.
In posting to the castle group to see if their dynamic proxy sufferred from the same issue Ayende asked if its like ref and out and the answer is sort of... Basically a modopt is like a custom attribute on a parameter to a method that can be emitted by a compiler. The big difference between the modopt and the attribute is that the modopt actually changes the signature of the method as far as the CLS is concerned. They are however very similar to out/ref in that the callee must specify it when calling and the fact that they change the signature of the method.
From ECMA 335 (7.1.1): "Two signatures that differ only by the addition of a custom modifier (required or optional) shall not be considered to match."
C++/CLI uses modopts fairly extensively but I have never really proxied many C++/CLI classes.
OK on with the modopt stuff
Well in seeing this I was quickly able to create two failing unit tests for my dynamic proxy. The first unit test involves overloading based only on a modopt difference. Let's propose I have the following two methods (as defined in spec#)
void Foo(string s) {}
and
void Foo(string! s) { } //notnull string
When I compile this code I receive method signatures as follows.
.method public hidebysig instance void OverloadByModOpt(string foo) cil managed
.method public hidebysig instance void OverloadByModOpt(string modopt([System.Compiler.Runtime]Microsoft.Contracts.NonNullType) foo) cil managed
As you can see the only difference between the two signatures is the modopt of being a non-null type. My dynamic proxy at present was not aware of modopt and therefor would generate an invalid class as it would use the same signature twice.
The second problem is when you call a method with a modopt. Let's imagine that the overload without the modopt no longer exists and I am just calling the version with the modopt. Because it varies the signature you have to actually include it in the signature of your call. Since I was not doing this I would receive a runtime method not found exception. An example of calling the method with the modopt:
call instance void ClassLibrary1.Class1::OverloadByModOpt(string
modopt([System.Compiler.Runtime]Microsoft.Contracts.NonNullType))
What about C#?
Well if you go look at ECMA 334 (the C# spec) and you search for the word "modopt" you will find 0 instances. The C# specification does not actually mention modopts in any way. I have already posted something about this to MS. In the case that C# is dealing with a modopt varied overload it will prefer the version without the modopt.
One item which I have not yet had time to test (someone else feel free) is to give C# two methods both with different modopts to see which one it chooses. My guess is that it will give an error or it will just take the first one it sees (hopefully it gives an error).
Obviously there are rules associated with the handling of choosing an overload here they are just unfortunately missing from the spec. Because they are missing from the spec it is quite possible that that *insert other spec compliant implementation* could work in a completely different yet compliant manner.
I have at this point not heard back from the team on this but promise to update this post when I do.
Finally back to spec#
I mentioned as a spec# con in my presentation its use of modopt and people looked at me like I had 3 heads ... here is some further info.
The most interesting part of all of this is the spec# compiler which generated this code. It in fact suffers from the same problem as C# (which it not surprising since it is derived from C#). It will generate the overloaded method solely by modopt but will never generate a call to the modopt version of the method. In general the logic that C# ignores the optional modifier makes alot of sense since it does not understand it and the modifier is by its very definition 'optional'.
When we look at spec# however which is generating the modifer I would expect it to honor it in its overload resolution (it understands it since it generated it) or to not let me do it all. I personally prefer not at all (largely in part due to language interop reasons since some languages can't deal with these types of overloads, J# being an example but I think there are more developers at MS maintaining J# than there are professionals using it ... talk about a death march :)) I sent an email as such to the spec# list and was told that yes it should be considerred a bug and that the compiler would in the future not do this.
The interesting conclusion about all of this is that if the compiler won't let me overload based upon the modopt why use a modopt? The key difference between the modopt and an attribute is that it varies the signature allowing for overloading based upon it. If I can't overload might as well make it an attribute which alleviates all of the issues with my dynamic proxy (well atleast until something else with a modopt is proxied :))
Moral of the story
Modopt is obscure .. it is partially handled in various places. Using modopt will likely cause you problems interacting with other languages and/or environments (I believe mono did not understand modopt for a while). If you are writing a tool that deals with signatures DON'T FORGET MODOPT!
Posted
Fri, Jun 8 2007 5:30 PM
by
Greg