14

Consider the following types:

  • (int, int) → managed.
  • struct MyStruct { public (int,int) Value; } → unmanaged!

Problem: A non-generic structure MyStruct, which has a managed member (int,int) has been evaluated as managed type.

Expected Behavior: A structure which contains a managed member, should be considered as managed, the same way the struct MyStruct { int? Value; } are considered as managed.

It seems both types are behaving against the documentations [1] and [2].

Example 1 - unmanaged Constraint

class Program
{
    static void DoSomething<T>() where T : unmanaged { }
    struct MyStruct {  public (int, int) Value; }
    static void Main(string[] args)
    {
        DoSomething<MyStruct>();    // → OK
        DoSomething<(int, int)>();  // → Shows compile-time error
    }
}

Error CS8377 The type '(int, int)' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Program.DoSomething()'

Example 2 - pointer or sizeof

Using above structure, the behavior is the same for pointers or sizeof operator:

unsafe 
{
    (int, int)* p1;  // → Compile-time error, 
    MyStruct* p2;    // → Compiles
}

Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type('(int, int)')

Question

  1. How do a struct containing ValueTuple is considered as unmanaged and can satisfy unmanaged constraint while the ValueTuple is considered as managed?

  2. How a struct having ValueTupple<T1, T2> and a struct containing Nullable<T> are treated differently?


Note 1: IMO the issue is different from the Proposal: Unmanaged constructed types (addressed by DavidG in comments), because MyStruct is not generic, on the other hand while int? and (int,int) both are managed, but struct MyStruct { int? Value; } and struct MyStruct { (int, int) Value; } evaluated differently.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Yeah, but that issue is about unmanaged constructed type, and I guess tuples fall into that same bracket. – DavidG Jan 01 '19 at 04:14
  • Well, I read that issue as a bug in the compiler that needs to be fixed. – DavidG Jan 01 '19 at 04:22
  • Fun fact: Resharper sees this first line (`DoSomething()`) as the same error. – DavidG Jan 01 '19 at 04:26
  • Of course it's a different issue, I just thought it was interesting to share. – DavidG Jan 01 '19 at 04:36
  • I believe this is a compiler error. I'd recommend opening an issue on the Rosyln repo – John Jan 01 '19 at 14:10
  • Was my comment here linking to the Github issue really flagged and deleted? – DavidG Jan 01 '19 at 16:59
  • @DavidG Feel free to post your comment again if you think it's required. I removed my comments as well since they are no longer required. – Reza Aghaei Jan 01 '19 at 17:03
  • I'm less bothered that they were flagged, more concerned that a mod decided it was valid to be removed. – DavidG Jan 01 '19 at 17:05
  • Especially since I still believe it's the same issue that you are experiencing. – DavidG Jan 01 '19 at 17:06
  • @DavidG I agree about your point about flagging. But disagree about being the same issue (while they are related and may have the same root cause.) Flagging was done by me because based on your comment '*Of course it's a different issue, I just thought it was interesting to share.*' I thought we are agreed about begin a different issue and so these comments are no longer needed. Probably moderator also had the same understanding as me. Anyway, I'm sorry if it offended you, it was not intentional, it was a misunderstanding. – Reza Aghaei Jan 01 '19 at 17:16
  • @DavidG If for any reason you would like to post the comment again, let's clean up the comments and post your comment again. I appreciate your input about the question. – Reza Aghaei Jan 01 '19 at 17:18
  • I've summarized question(s) at the bottom of the post. – Reza Aghaei Jan 01 '19 at 17:35
  • My "different issue" comment was saying that the Resharper issue is different from what you were asking in your post. I'm not offended, just a little uncomfortable that a moderator would remove comments that potentially provide valid information. – DavidG Jan 01 '19 at 18:04
  • As for why I still think it's the same issue, a tuple is a constructed type (`(int, int)` is the same as `ValueTuple`) and why I think the bug in the compiler is picking up the *root* type as being constructed. Speculation of course, but I'd be surprised if you opened an issue and it didn't get closed as a duplicate. – DavidG Jan 01 '19 at 18:06
  • @DavidG I see, probably they had the same misunderstanding and like me they also thought we no longer need it. Anyway, at the bottom of the post I mentioned your comment. – Reza Aghaei Jan 01 '19 at 18:11
  • @DavidG The compiler is using different rules for a struct having `int?` field and a struct having `(int, int)` fields, while both of them are generic structs. Maybe because `int?` is special *nullable* struct. – Reza Aghaei Jan 01 '19 at 18:18

1 Answers1

9

Thanks for reporting. This is just a bug in the compiler. The tuple when used as a field should be registering as a generic type and hence invalid in an unmanaged type. It appears to be evaluating as a tulpe instead and missing this check.

Good news is that in C# 8.0 this restriction will be going away. The type (int, int) is a valid unmanaged type.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Thanks! *It appears to be evaluating as a `Tuple` ...* → Do you mean evaluated as a `non-generic struct`? because if it's evaluating as `Tuple/ValueTuple`, since they are managed types, then `struct MyStruct { (int,int) Value; }` should be evaluated as `managed` while it's been evaluated as `unmanaged`. – Reza Aghaei Jan 03 '19 at 04:48