csharplang icon indicating copy to clipboard operation
csharplang copied to clipboard

Champion "blittable"/"unmanaged" constraint (15.7)

Open gafter opened this issue 8 years ago • 56 comments

The blittable feature will give language enforcement to the class of types known as "unmanaged types" in the C# language spec. This is defined in section 18.2 as a type which is not a reference type and doesn't contain reference type fields at any level of nesting.

Allows where T : unmanaged

  • [x] Proposal added (https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/blittable.md)
  • [x] Discussed in LDM
  • [ ] Decision in LDM
  • [ ] Finalized (done, rejected, inactive)
  • [ ] Spec'ed

gafter avatar Feb 26 '17 19:02 gafter

Is this a proposal to improve existing blittable types or something? what's this about? :)

iam3yal avatar Feb 27 '17 09:02 iam3yal

@eyalsk It is [Redacted] ::)

vbcodec avatar Feb 27 '17 10:02 vbcodec

Is this related to https://github.com/dotnet/roslyn/issues/2209 ?

ufcpp avatar Feb 27 '17 12:02 ufcpp

Sorry for the confusion. Will reopen when we have the proposal ready.

jaredpar avatar Feb 27 '17 16:02 jaredpar

Sorry there isn't more details here. This is being used as a reminder for me to add more details.

jaredpar avatar Feb 27 '17 17:02 jaredpar

Proposal added here https://github.com/dotnet/csharplang/pull/206

jaredpar avatar Feb 28 '17 04:02 jaredpar

This is good idea! but I have some questions

  • How is this related to Blittable Types in Interop Marshaling? The same? or similar but bit different?
  • Can IntPtr and UIntPtr be blittable? They are blittable in the Interop Marshaling and unmanaged in the current C# spec.

ufcpp avatar Feb 28 '17 04:02 ufcpp

@jaredpar Just a thought.

Why can't it be an attribute?

[Blittable]
struct Point 
{
    public int X;
    public int Y;
}

And instead of adding a new blittable constraint such in the following case:

void Hash<T>(T value) where T : blittable struct
{
    using (T* p = &value) { 
        ...
    }
}

Allow attributes to be used as a generic constraint? so something like this.

void Hash<T>(T value) where T : struct, Blittable
{
    using (T* p = &value) { 
        ...
    }
}

This seems like a more general feature that can be used here and elsewhere in the framework. :)

Alternatively, use a marker interface?

iam3yal avatar Feb 28 '17 07:02 iam3yal

@bbarry Why the downvote? what's the point of adding a new keyword? and then adding a special constraint when existing features can actually do the job or/and improved? not to mention that blittable struct is at odds with existing constraints, for example, why can't we check that the generic parameter is abstract class? what makes blittable any special?

Maybe I'm overlooking something so if you can, please do elaborate. :)

iam3yal avatar Mar 01 '17 07:03 iam3yal

@eyalsk I think it's reasonable as a keyword, because adding it to a structure changes its compile-time behaviour (it means adding non-blittable fields will fail to compile). It seems like a nice fit to me.

KodrAus avatar Mar 01 '17 07:03 KodrAus

@eyalsk, it is nothing personal, my downvote is just that I'd rather see this be a keyword over an attribute. It is admittedly a fine line in the distinction (surely the compiled code would contain some attribute so that this can be enforced across assemblies, so the compiler must in fact know about that as it is enforced).

Adding a special case to permit the use of one particular attribute for this purpose seems arbitrary and counter to syntax consistency and rules that devs can internalize when writing code (I dislike all the attributes that do this already). Allowing general constraints in the form where T : Something where Something maps to an attribute type SomethingAttribute which must be defined on the type T is at first mildly interesting (I am not sure it has advantages over a marker interface, but I don't really like those), but is significantly a wider scope than this proposal and at best should be separate.

My gut says the former is yucky and the latter is going to introduce either ambiguities or breaking changes. A keyword isn't significantly better, but I can reasonably expect that a keyword means something to a compiler and will have an impact on how my code builds.


If there were the ability to have generalized attribute constraints, Blittable would still need to be handled in a special way by the compiler because it must enforce certain constraints on the struct type where it is used (for example disallowing non-blittable fields or auto struct layout).

bbarry avatar Mar 01 '17 14:03 bbarry

@KodrAus, @bbarry Okay, fair enough, thank you both for the explanation. :)

iam3yal avatar Mar 01 '17 14:03 iam3yal

The name blittable is a bit weird. To newcomers it doesn't remotely convey what it means. I'm not convinced that "blitting" is a known concept outside of a specific subset of Win32 developers who happened to know that "BitBlt" inspired a verb, and I'm one of them.

The name unmanaged is a little better, but it doesn't really grab me. I don't know that I have a better option. Maybe fixed, but that seems to imply more than this does as well.

Also, how is the generic constraint enforced? I don't see a mention of a CLR change to accompany this, so would this be encoded on the generic type/method as an attribute and enforced only by supporting compilers? What would happen if an errant compiler were to use one of these generic types/methods specifying a non-blittable struct generic type argument?

HaloFour avatar Mar 01 '17 15:03 HaloFour

@HaloFour

What would happen if an errant compiler

This problem already exists in the current C# compiler. Using the Unsafe class, managed types can be converted to pointer types. This could cause GC crash.

using System;
using System.Runtime.CompilerServices;

struct UnmanagedStruct
{
    public int X;
    public int Y;
    public override string ToString() => (X, Y).ToString();
}

struct ManagedStruct
{
    public string X;
    public string Y;
    public override string ToString() => (X ?? "NULL", Y ?? "NULL").ToString();
}

unsafe class Program
{
    static void Main()
    {
        // OK
        // An unmanaged type can be used with pointers
        UnmanagedStruct us = new UnmanagedStruct { X = 1, Y = 2 };
        var usp = (int*)Unsafe.AsPointer(ref us);

        usp[0] = 100;
        usp[1] = 200;
        Console.WriteLine(us); // (100, 200)

        // OK!
        // This is an abuse of the Unsafe class.
        // The Unsafe class is implemented by IL and allows some problematic codes.
        // For instance, it can make managed type pointer.
        ManagedStruct ms = new ManagedStruct { X = "abc", Y = "cde" };
        var msp = (IntPtr*)Unsafe.AsPointer(ref ms);

        msp[0] = (IntPtr)0;
        msp[1] = (IntPtr)0;
        Console.WriteLine(ms); // (NULL, NULL)

        msp[0] = (IntPtr)1234567; // invalid pointer. GC could be crashed
    }
}

ufcpp avatar Mar 01 '17 16:03 ufcpp

Related to https://github.com/dotnet/corefxlab/blob/e109353256875ee42e6365c5facd243b1f0497ce/src/System.Slices/System/SpanExtensions_binary.cs#L21

ufcpp avatar Mar 01 '17 16:03 ufcpp

@ufcpp

Yes, but the existing code doesn't have any declared "constraints" intended to prevent that so the developer is definitely walking into unsafe territory there.

HaloFour avatar Mar 01 '17 16:03 HaloFour

@ufcpp

How is this related to Blittable Types in Interop Marshaling? The same? or similar but bit different?

It's the same.

Can IntPtr and UIntPtr be blittable? They are blittable in the Interop Marshaling and unmanaged in the current C# spec.

Yes these are blittable. I missed that in the proposal. Thanks!

jaredpar avatar Mar 01 '17 17:03 jaredpar

@eyalsk

Why can't it be an attribute?

In general the C# language avoids attaching semantics to attributes attached to types / methods. The preference is to use explicit language syntax to implement semantic changes in behavior.

jaredpar avatar Mar 01 '17 17:03 jaredpar

@jaredpar Understood, thanks. :)

iam3yal avatar Mar 01 '17 17:03 iam3yal

@HaloFour

The name blittable is a bit weird. The name unmanaged is a little better, but it doesn't really grab me.

Yep ... naming is hard. 😄

One of the other names we considered in Midori was primitive.

jaredpar avatar Mar 01 '17 18:03 jaredpar

I like blittable. My estimate of developers would be more liberal, but it's easy enough to Google if you are unfamiliar with a term. The win is that blittable is the most directly precise term.

jnm2 avatar Mar 01 '17 18:03 jnm2

@jaredpar

Agreed, and it's a minor point at least at this stage of the proposal.

@jnm2

Even if that were the case, blit by itself doesn't mean anything. It happens to be the pseudo-pronouciation given to 1970s-era graphics compositing routines that Win32 happened to inherit but are largely obsolete today. We might as well call them sprite structs. I'd rather a name that better captures the intent of constraining the type to primitives or composites of primitives.

HaloFour avatar Mar 01 '17 19:03 HaloFour

I've often seen "blit" used without a graphics connotation, more of a "spit out these raw bytes fast without parsing" connotation which is even slightly contradictory to the original meaning. I believe blit by itself has come to have a meaning of its own distinct from its origins (as all words do).

jnm2 avatar Mar 01 '17 21:03 jnm2

Yeah, 'blittable' means 'can be safely treated as a single contiguous chunk of bits' to me.

orthoxerox avatar Mar 01 '17 21:03 orthoxerox

How is this related to Blittable Types in Interop Marshaling? The same? or similar but bit different?

It's the same

@jaredpar Almost but not quite the same. Unfortunately bool isn't blittable according to marshaling but it is according to this proposal.

mikedn avatar Mar 03 '17 19:03 mikedn

flat?

omariom avatar Mar 09 '17 00:03 omariom

'plain' perhaps e. g. 'plain struct' or 'pure', but 'blittable' doesn't work we've had that discussion many times before. I think I made a list of possible names at some point, can't find it though. 😕

nietras avatar Apr 24 '17 17:04 nietras

I don't know but maybe interoperable struct? yes it's a bit long but at least people would understand what it is or at least where it should be used? alternatively interop struct?

iam3yal avatar Apr 26 '17 01:04 iam3yal

I was propose #144 that, in my opinion, struct should always automatically inherit the intersection of the kind of any fields it contains

If any struct contain only blittable, then it should also be blittable

could we have compiler automatically attach attribute or derived from difference type than ValueType (make Primitive type, Integer type, Number type, Blittable type, etc. derived from ValueType?)

Thaina avatar Apr 26 '17 07:04 Thaina

Blittable is used in a large majority of the existing MSDN documentation (example: https://msdn.microsoft.com/en-us/library/75dwhxf7(v=vs.110).aspx), so I think it makes the most sense. Anyone who does large amounts of Interop (or who has read the CLR Interop documentation) should be familiar with the term.

Also, bool, char, and string are special. They are considered "sometimes blittable" and have special handling in the runtime. For example, char is blittable when the type is 2-bytes on both sides (sometimes char needs to be 1 or 4 bytes, however) and bool is blittable when the type is 1-byte on both side (most of the time, for Windows interop, bool needs to be 4-bytes, however). In certain cases, string will also be pinned and marshaled directly as well (rather than copied and then marshaled).

tannergooding avatar Apr 26 '17 17:04 tannergooding