The implementation semantics of thread static are below the IL level, in the .NET jit compiler. Compilers that emit to IL like VB.NET and C# don't need to know anything about Win32 TLS in order to emit IL code that can read and write a variable that has the ThreadStatic attribute. There's nothing special about the variable as far as C# knows - it's just a location to read and write stuff. The fact that it has an attribute on it is of no consequence to C#. C# only needs to know to emit IL read or write instructions for that symbol name.
The 'heavy lifting' is done by the core CLR that is responsible for making the IL work on a particular hardware architecture.
That would also explain why putting the attribute on an inappropriate (non-static) symbol doesn't get a reaction from the compiler. The compiler doesn't know what special semantics the attribute requires. Code analysis tools like FX/Cop, though, should know about it.
Another way to look at it: CIL defines a set of storage scopes: static (global) storage, member storage, and stack storage. TLS isn't on that list, very likely because TLS doesn't need to be on that list. If IL read and write instructions are sufficient to access TLS when the symbol is tagged with a TLS attribute, why should IL have any special representation or treatment for TLS? It's not needed.
public class SomeClass { public static MyContext myContext; ...
Now immediately after grabbing a pool thread, doSomeClass.myContext = new MyContext();
– Forgetful