First, .NET structs are flattenable but they're not value types, which are immutable and so pose further problems. But the two main challenges for adding any new big feature to Java are forward compatibility and simplicity.

1. Forward compatibility: While backward compatibility means that old code continues to run and so is easy to do (add the new feature "on the side"), forward compatibility, or migration compatibility, means making it easy for old code to take advantage of new features with little or no change.

2. Simplicity: For every feature (added to help, say, efficiency) that makes an advanced developer happy you risk scaring away ten less-advanced developers. Complex languages are also rarely taught as first languages, which makes it very hard for them to reach or remain at the very top of the most popular languages. Eventually every language faces requirements that demand a new feature that makes the language more complex, but if great care is not taken to control the added complexity as much as possible (or even give up on the feature as not being worth the cost to complexity), the languages eventually faces a threat to popularity.

While Java has always cared about these two, .NET not so much (maybe they're right not to, but in any event that's a real philosophical/cultural difference between the two platforms). For example, to make simple blocking enjoy the scalability benefits of async/await you need to change a lot of things; that wasn't acceptable for us. And MS has always been a fan of rather complex languages (with the exception of VB); as early as the late eighties MS was "the C++" company.

Now, to be more specific, one of the big challenges of value types is object initialisation, which is what John's article is primarily about. If you create an array of a value type, the elements must be initialised to some value which isn't null. How do you express that in a way that is relatively forward compatible, simple, but also efficient? Furthermore, as John points out, even for value classes that don't admit a "zero" default value, how do you express the constructor for an immutable type that can be flattened? It's easy to do this for reference records because reference types are always "published" to readers with an atomic write of the pointer, but value types (which, again, are immutable) need to initialise their fields atomically even when flattened.

BTW, generic erasure is not a big challenge. While value types will eventually (in a later phase) require specialised generics (because you want, say, an ArrayList of a complex number value type internally use a flattened array), adding that is not a huge problem for forward compatibility and simplicity because value types are invariant, i.e. they cannot subclass nor be subclassed, so the complexity and forward compatibility issues that reification brings to variant types aren't as bad (the problem with reifying generics that can be variant is that you need to bake a particular language's variance strategy into the runtime itself). Erased generics have so far helped Java much more than they hurt it because we keep seeing their forward-compatibility benefits over and over (including for future features that we're thinking about). They also help simplicity because they make the runtime an attractive compilation target for complex languages that may draw those who prefer such languages while keeping them on the platform and at the same time reducing the pressure on the Java to add complex features that could threaten its popularity among the majority who prefer simpler languages.