New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
gafter opened this Issue Feb 26, 2017 · 51 comments

Comments

Projects
None yet
@gafter
Copy link
Member

gafter commented Feb 26, 2017

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

@gafter gafter added this to the 7.2 candidate milestone Feb 26, 2017

@eyalsk

This comment has been minimized.

Copy link
Contributor

eyalsk commented Feb 27, 2017

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

@vbcodec

This comment has been minimized.

Copy link

vbcodec commented Feb 27, 2017

@eyalsk It is [Redacted] ::)

@ufcpp

This comment has been minimized.

Copy link

ufcpp commented Feb 27, 2017

Is this related to dotnet/roslyn#2209 ?

@jaredpar

This comment has been minimized.

Copy link
Member

jaredpar commented Feb 27, 2017

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

@jaredpar jaredpar closed this Feb 27, 2017

@jaredpar jaredpar reopened this Feb 27, 2017

@jaredpar

This comment has been minimized.

Copy link
Member

jaredpar commented Feb 27, 2017

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

@jaredpar

This comment has been minimized.

Copy link
Member

jaredpar commented Feb 28, 2017

Proposal added here #206

@ufcpp

This comment has been minimized.

Copy link

ufcpp commented Feb 28, 2017

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.
@eyalsk

This comment has been minimized.

Copy link
Contributor

eyalsk commented Feb 28, 2017

@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?

@eyalsk

This comment has been minimized.

Copy link
Contributor

eyalsk commented Mar 1, 2017

@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. :)

@KodrAus

This comment has been minimized.

Copy link

KodrAus commented Mar 1, 2017

@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.

@bbarry

This comment has been minimized.

Copy link
Contributor

bbarry commented Mar 1, 2017

@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).

@eyalsk

This comment has been minimized.

Copy link
Contributor

eyalsk commented Mar 1, 2017

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

@HaloFour

This comment has been minimized.

Copy link
Contributor

HaloFour commented Mar 1, 2017

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?

@ufcpp

This comment has been minimized.

Copy link

ufcpp commented Mar 1, 2017

@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
    }
}
@HaloFour

This comment has been minimized.

Copy link
Contributor

HaloFour commented Mar 1, 2017

@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.

@jaredpar

This comment has been minimized.

Copy link
Member

jaredpar commented Mar 1, 2017

@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

This comment has been minimized.

Copy link
Member

jaredpar commented Mar 1, 2017

@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.

@eyalsk

This comment has been minimized.

Copy link
Contributor

eyalsk commented Mar 1, 2017

@jaredpar Understood, thanks. :)

@jaredpar

This comment has been minimized.

Copy link
Member

jaredpar commented Mar 1, 2017

@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.

@jnm2

This comment has been minimized.

Copy link
Contributor

jnm2 commented Mar 1, 2017

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.

@HaloFour

This comment has been minimized.

Copy link
Contributor

HaloFour commented Mar 1, 2017

@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.

@jnm2

This comment has been minimized.

Copy link
Contributor

jnm2 commented Mar 1, 2017

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).

@orthoxerox

This comment has been minimized.

Copy link

orthoxerox commented Mar 1, 2017

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

@MadsTorgersen MadsTorgersen modified the milestones: 7.2 candidate, 7.X candidate Oct 2, 2017

@MarkPflug

This comment has been minimized.

Copy link

MarkPflug commented Oct 5, 2017

Several people have expressed displeasure with the blittable keyword, and I think I agree; it feels weird for some reason. What about value struct? value is already a contextual keyword, can it be reused in this context? value feels pretty accurate to me, in that it is a struct that contains no "refs". Maybe I'm wrong about my understanding of this though. Or possibly value wouldn't work because it would cause ambiguity in the language somewhere.

@alrz

This comment has been minimized.

Copy link
Contributor

alrz commented Oct 5, 2017

static delegates are blittable,

static delegate int Func();

So I'd say static struct is consistent in that manner, at least.

@tannergooding

This comment has been minimized.

Copy link
Member

tannergooding commented Oct 5, 2017

Some structs that contain references can still be blittable (strings, once pinned, and assuming they are passed as readonly are blittable, for example).

I still think that blittable matches all the existing documentation on the subject and is easily found if unfamiliar (top result for "blittable" on both Bing and Google).

I think that most of the other suggestions come close, but miss scenarios that would otherwise be ok.

@alrz

This comment has been minimized.

Copy link
Contributor

alrz commented Oct 5, 2017

blittable has the same problem that supersede modifier had: looks like we took it from Eminem's lyrics.

Seriously though, @tannergooding, do you think static struct vs static delegate doesn't make sense neither? Regarding documentations on the subject, we did this with struct and class for value vs. reference types. I don't think we should reflect the exact wording of the documentation, rather we should strive for consistency in the language, IMO.

@jnm2

This comment has been minimized.

Copy link
Contributor

jnm2 commented Oct 5, 2017

public static class Foo => public static struct Foo => wheee!

@MarkPflug

This comment has been minimized.

Copy link

MarkPflug commented Oct 5, 2017

Some structs that contain references can still be blittable (strings, once pinned, and assuming they are passed as readonly are blittable, for example).

Is that not true for any pinned reference, a pinned byte[] array? Or are strings truly special in this regard? Wouldn't this just be a effectively a void* or a IntPtr then? Still sounds "value-ish" to me.

@jnm2

This comment has been minimized.

Copy link
Contributor

jnm2 commented Oct 5, 2017

Wouldn't this just be a effectively a void* or a IntPtr then? Still sounds "value-ish" to me.

To me, value-ish, no. Blit-ish, yes. For what it's worth. 😄

I think blit has more apropos meaning in people's minds (“this blob can be used without transformation”) compared to value (“this structure has value semantics rather than identity semantics”). or even val which some propose to mean readonly var.

@tannergooding

This comment has been minimized.

Copy link
Member

tannergooding commented Oct 5, 2017

@alzr, I'm not convinced static delegate is right either. I had initially proposed value delegate, but some discussion ended up changing that (I don't recall what it was at the time).

Code that you aren't familiar with is going to be searched and static struct will likely cause ambiguity and confusion. I think static struct will cause large amounts of confusion, especially when compared to static class.

@nietras

This comment has been minimized.

Copy link

nietras commented Mar 15, 2018

Should the title of this not be renamed to lose the "blittable" naming now that the wording has, thank god, become unmanaged? 😄

@jcouv jcouv changed the title Champion "blittable" Champion "blittable"/"unmanaged" constraint (15.7) Mar 16, 2018

@OmarTawfik OmarTawfik assigned gafter and unassigned OmarTawfik Apr 3, 2018

@OmarTawfik

This comment has been minimized.

Copy link
Contributor

OmarTawfik commented Apr 3, 2018

Assigning back to champion since implementation is done.

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented Aug 21, 2018

Taking a pointer to a generic with unmanaged constraint (or indirectly on a struct that is implicitly unmanaged transitively as for issue #1504) is the very primary usage of the unmanaged constraint. I can only consider the unmanaged constraint done if T* and fixed on T are allowed at C# level. This is possible at the IL level, this should be unlocked at C# level so that we can get rid off once and for good of the IL tricks we have been using for years to workaround this problem. It should allow to remove a dependency to System.Runtime.CompilerServices.Unsafe.
Is there any thing blocking this at the language spec and furthermore at the roslyn level?

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented Aug 21, 2018

Oops, nevermind, retrying this with latest and it seems that pointers/fixed are supported. The only remaining problem is #1504 which can be very annoying but less critical

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment