6

example:

struct Id<T> {
  int id;
}

struct Thing {
  public Id<Thing> id;
}

this causes a cyclic struct layout, but i don't see the cycle. if Id had a field of type T, sizeof would be undefined, but it doesn't.

is this a mono bug, or part of the spec?

notallama
  • 1,069
  • 1
  • 8
  • 11
  • Compiles fine in the MS compiler, so unless it's undefined in the spec, one of the two implementations is wrong. – Servy Apr 07 '15 at 15:22
  • 1
    There is no actual instance of `Thing` in `Id` so there is no cycle. – Matthew Watson Apr 07 '15 at 15:23
  • @MatthewWatson Correct, so the question is why it's failing to compile with a reason that doesn't actually apply. The OP is asserting it should compile and run, but mono is erroring when compiling it. – Servy Apr 07 '15 at 15:24
  • @MatthewWatson mono compiler indeed fails to compile this code. – Andrey Apr 07 '15 at 15:25
  • Yes, therefore it must be a mono bug I assume. – Matthew Watson Apr 07 '15 at 15:25
  • @MatthewWatson Either that or the spec says it should error and the MS compiler is the one violating the spec. – Servy Apr 07 '15 at 15:25
  • 3
    Actually, while it compiles in the MS compiler, it doesn't actually *run* (`TypeLoadException`, "Couldn't load type ..."). It doesn't seem to be a valid type definition. – Luaan Apr 07 '15 at 15:25
  • Mono bug report here: http://lists.ximian.com/pipermail/mono-bugs/2009-August/091456.html - But there's something weird in the MS compiler too as Luaan points out. – Matthew Watson Apr 07 '15 at 15:26
  • So it seems that the Mono compiler is aware that it cannot handle this (even though it doesn't really contain a cycle), so it warns about it; meanwhile the MS compiler doesn't say anything - but it can't handle it either. The code is nonsensical, so I guess Mono is actually doing the right thing. – Matthew Watson Apr 07 '15 at 15:30
  • @MatthewWatson More or less. The compiled DLL actually has the type, and it looks fine, it passes IL validation, the decompiled code isn't weird at all... but the CLR isn't actually able to load that type. – Luaan Apr 07 '15 at 15:32
  • I'd be interested to see what Roslyn does with this. :) Incidentally, [here's some discussion on this from 2008](https://social.msdn.microsoft.com/Forums/vstudio/en-US/2ddd1a00-1947-4187-b68b-7820c7b1bd69/typeloadexception-with-recursive-structs-and-generics-is-this-a-clr-bug?forum=clr) I'm now convinced that it's a problem with the MS compiler not reporting an error. – Matthew Watson Apr 07 '15 at 15:33
  • Also note that it only happens when *both* the `Id``1` and the `Thing` types are `struct`s. If you change one of them to `class`, it works as expected. – Luaan Apr 07 '15 at 15:40
  • If the structure directly or indirectly contained a variable of type `T` (note that `T[]` would be fine), there would obviously be a problem, since it would not be possible to determine a unique size for the structures involved. If the type loader realized that the structure did not directly or indirectly contain a variable of type `T`, it could accept such a structure without difficulty. I suspect the problem is that the type loader makes its determination of what to accept or reject based on a shallow level of inspection which can't tell whether a reflexive type would be a problem. – supercat Apr 07 '15 at 16:23
  • Corner cases like that can present an interesting problem for language implementers, since it's entirely possible that the language spec would say such a construct is legal even though the Runtime won't accept it. Personally, I would say that if the language spec says something is legal but the Runtime doesn't, the language spec is defective. Others, however, might view the language spec as fine and the runtime as defective. – supercat Apr 07 '15 at 16:30
  • Even more fun, if you compile this with the Microsoft compiler you can *run* it with mono. – yoyo Oct 20 '16 at 00:19
  • NOTE: I'm using the Mono runtime that comes with Unity 5.4.0f3; mono.exe reports "Mono JIT compiler version 2.0". – yoyo Oct 20 '16 at 00:25
  • And FWIW, Microsoft ngen (Native Image Generator, basically pre-JIT) reports the same type load error. – yoyo Oct 20 '16 at 00:31

1 Answers1

1

As discussed in comments, while this code compiles using the MS C# compiler, it doesn't actually execute - it gives a TypeLoadException in runtime. Note that the problem only appears when both the types are struct. So the question is, is this a problem of the C# compiler or the runtime?

Since the runtime is also covered by its own specification, I went through all the pieces of the CLI specification that were even vaguelly relevant, and I didn't find anything that would prohibit this. Not in IL definition (obviously, since the IL is considered valid), and not in the runtime metadata structures.

Given this, I'm more in favour of calling the runtime implementation flawed. I suspect that when the Mono team was faced with this issue, they considered adding a compiler error for this situation being the lesser evil. Or maybe they just evaluate the cyclic struct constraint incorrectly :)

It might even be possible that it didn't use to crash in runtime, making the C# compiler even more right. I have no way of verifying this, of course :)

Sadly, this means that you can't use that handy construct of yours. Either make sure one of the types is a class, or you'll just have to make a different type for each of those IdOfSomething of yours. Just be glad the Mono C# compiler told you so before you found out in runtime :P

Luaan
  • 62,244
  • 7
  • 97
  • 116