Skip to content

Auto-default structs cause runtime exceptions in explicit constructors when a ref field is not initialized #63018

@TessenR

Description

@TessenR

Version Used:

Microsoft Visual Studio Professional 2022
Version 17.3.0 Preview 3.0
VisualStudio.17.Preview/17.3.0-pre.3.0+32708.82
Microsoft .NET Framework
Version 4.8.04084

Steps to Reproduce:

Compile and run the following code:

using System;

var x = 42;
scoped var r = new R();
r.field = ref x;

ref struct R
{
  public ref int field;

  public R()
  {
    Console.WriteLine("explicit ctor");
  }
}

Expected Behavior:
The code runs without exceptions.

Actual Behavior:
The code crashes with a NullReferenceException in public R() constructor

Notes:
The compile emits the following IL for public R():

  .method public hidebysig specialname rtspecialname instance void
    .ctor() cil managed
  {
    .maxstack 8

    // [12 3 - 12 4]
    IL_0000: nop

    IL_0001: ldarg.0      // this
    IL_0002: ldfld        int32& R::'field'
    IL_0007: ldc.i4.0
    IL_0008: stind.i4

    // [13 5 - 13 40]
    IL_0009: ldstr        "explicit ctor"
    IL_000e: call         void [System.Console]System.Console::WriteLine(string)
    IL_0013: nop

    // [14 3 - 14 4]
    IL_0014: ret

  } // end of method R::.ctor

Which tries to store 0 value in the unassigned ref int field field resulting in a NullReferenceException
It seems that Roslyn should treat ref fields as references and leave null values if such fields aren't assigned in a constructor.

Interestingly, if you remove the constructor completely and use default struct constructor the code runs correctly, i.e. code gen that "zeroes" struct memory runs differently in default struct constructor and any explicit one that doesn't initialize a byref field.

Note that the current behavior limits usability of other features related to low level structs, e.g. you can assign ref fields via object initializers but the current behavior forces you to assign a reference in the struct constructor first only to be immediately overridden via an object initializer

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Active/Investigating

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions