Incorrect compiler cast with abstract collection
Following code used to run correctly in previous X# builds, but at least starting with 2.20 (or a build prior to that), it stopped, now throwing an ArgumentOutOfRangeException:
CLASS KeyedCollectionSubclass INHERIT System.Collections.ObjectModel.KeyedCollection<INT, STRING>
PROTECTED METHOD GetKeyForItem(o AS STRING) AS INT
RETURN 123
END CLASS
FUNCTION Start() AS VOID
LOCAL col AS KeyedCollectionSubclass
col := KeyedCollectionSubclass{}
col:Add("abc")
LOCAL o AS OBJECT
// this throw an index out of range exception in 2.20
o := col[123] // abc in 2.14
? o
// this works in 2.20, but it is wrong
o := col[0] // abc in 2.20
? o
The problem is that in recent builds, the compiler maps the index expression to the indexer of the System.Collections.ObjectModel.Collection class (from which KeyedCollection inherits from) and not to the indexer of the System.Collections.ObjectModel.KeyedCollection class anymore, as it used to in older builds.
(correct) code generated by build 2.14:
KeyedCollectionSubclass col = new KeyedCollectionSubclass();
col.Add("abc");
object o = ((KeyedCollection<int, string>)col)[123];
IL_0001: newobj instance void KeyedCollectionSubclass::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "abc"
IL_000d: callvirt instance void class [mscorlib]System.Collections.ObjectModel.Collection`1<string>::Add(!0)
IL_0012: nop
IL_0013: ldloc.0
IL_0014: ldc.i4.s 123
IL_0016: callvirt instance !1 class [mscorlib]System.Collections.ObjectModel.KeyedCollection`2<int32, string>::get_Item(!0)
(incorrect) code produced by build 2.20:
KeyedCollectionSubclass col = new KeyedCollectionSubclass();
col.Add("abc");
object o = ((Collection<string>)col)[123];
IL_0001: newobj instance void KeyedCollectionSubclass::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "abc"
IL_000d: callvirt instance void class [mscorlib]System.Collections.ObjectModel.Collection`1<string>::Add(!0)
IL_0012: nop
IL_0013: ldloc.0
IL_0014: ldc.i4.s 123
IL_0016: callvirt instance !0 class [mscorlib]System.Collections.ObjectModel.Collection`1<string>::get_Item(int32)
IL_001b: stloc.1
IL_001c: ldstr "\r\n{0}"
IL_0021: ldloc.1
IL_0022: call void [mscorlib]System.Console::Write(string, object)
IL_0027: nop
IL_0028: ldloc.0
IL_0029: ldc.i4.0
IL_002a: callvirt instance !0 class [mscorlib]System.Collections.ObjectModel.Collection`1<string>::get_Item(int32)
IL_002f: stloc.1
Guys,
Don't want to be a nuisance, but not sure to what extend this issue has been discussed or investigated. I can understand if it is a c# issue, but could find nowhere that this issue was raised on any c#/.NET forum which makes me believe that this might be a lexer or compiler issue.
Johan
Hi Johan Yes, it's a compiler issue probably, but it's very difficult to find, as it's probably somewhere deep in the "communication" between the X# front end and the c# backend.
Can't you modify the code so it does not use a subclass of the KeyedCollection until this is fixed?
Found a workaround for this problem, instead of using the indexer directly, you can use it through the Item indexed property:
o := col:Item[123]
This one does work correctly as it gets resolved to the correct property.