Skip to content

Commit

Permalink
Auto-default ref fields with a null ref-assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Jul 29, 2022
1 parent 5de5410 commit 6218c6a
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 11 deletions.
24 changes: 19 additions & 5 deletions src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,25 @@ private static BoundBlock PrependImplicitInitializations(BoundBlock body, Method
var builder = ArrayBuilder<BoundStatement>.GetInstance(implicitlyInitializedFields.Length);
foreach (var field in implicitlyInitializedFields)
{
builder.Add(
F.ExpressionStatement(
F.AssignmentExpression(
F.Field(F.This(), field),
F.Default(field.Type))));
if (field.RefKind == RefKind.None)
{
// field = default(T);
builder.Add(
F.ExpressionStatement(
F.AssignmentExpression(
F.Field(F.This(), field),
F.Default(field.Type))));
}
else
{
// field = ref *default(T*);
builder.Add(
F.ExpressionStatement(
F.AssignmentExpression(
F.Field(F.This(), field),
F.NullRef(field.TypeWithAnnotations),
isRef: true)));
}
}
var initializations = F.HiddenSequencePoint(F.Block(builder.ToImmutableAndFree()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,7 @@ public override BoundNode VisitFixedStatement(BoundFixedStatement node)
Debug.Assert(!pinnedTemp.Type.IsManagedTypeNoUseSiteDiagnostics);

// temp = ref *default(T*);
cleanup[i] = _factory.Assignment(_factory.Local(pinnedTemp), new BoundPointerIndirectionOperator(
_factory.Syntax,
_factory.Default(new PointerTypeSymbol(pinnedTemp.TypeWithAnnotations)),
refersToLocation: false,
pinnedTemp.Type),
isRef: true);
cleanup[i] = _factory.Assignment(_factory.Local(pinnedTemp), _factory.NullRef(pinnedTemp.TypeWithAnnotations), isRef: true);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,13 @@ public BoundExpression Null(TypeSymbol type)
return Null(type, Syntax);
}

// Produce a ByRef null of given type, like `ref T Unsafe.NullRef<T>()`.
public BoundExpression NullRef(TypeWithAnnotations type)
{
// *default(T*)
return new BoundPointerIndirectionOperator(Syntax, Default(new PointerTypeSymbol(type)), refersToLocation: false, type.Type);
}

public static BoundExpression Null(TypeSymbol type, SyntaxNode syntax)
{
Debug.Assert(type.CanBeAssignedNull());
Expand Down
193 changes: 193 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12592,5 +12592,198 @@ public void ScopedReserved_Alias_Escaped()
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using @scoped = System.Int32;").WithLocation(1, 1)
);
}

[Fact, WorkItem(63018, "https://github.com/dotnet/roslyn/issues/63018")]
public void InitRefField_AutoDefault()
{
var source = """
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");
}
}
""";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor"));
verifier.VerifyIL("R..ctor()", @"
{
// Code size 19 (0x13)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: conv.u
IL_0003: stfld ""ref int R.field""
IL_0008: ldstr ""explicit ctor""
IL_000d: call ""void System.Console.WriteLine(string)""
IL_0012: ret
}");
}

[Fact, WorkItem(63018, "https://github.com/dotnet/roslyn/issues/63018")]
public void InitRefField_UnsafeNullRef()
{
var source = """
using System;

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

ref struct R
{
public ref int field;

public R()
{
field = ref System.Runtime.CompilerServices.Unsafe.NullRef<int>();
Console.WriteLine("explicit ctor");
}
}
""";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor"));
verifier.VerifyIL("R..ctor()", @"
{
// Code size 22 (0x16)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""ref int System.Runtime.CompilerServices.Unsafe.NullRef<int>()""
IL_0006: stfld ""ref int R.field""
IL_000b: ldstr ""explicit ctor""
IL_0010: call ""void System.Console.WriteLine(string)""
IL_0015: ret
}");
}

[Fact, WorkItem(63018, "https://github.com/dotnet/roslyn/issues/63018")]
public void InitRefField_AutoDefault_RefReadonly()
{
var source = """
using System;

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

ref struct R
{
public ref readonly int field;

public R()
{
Console.WriteLine("explicit ctor");
}
}
""";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();

var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor"));
verifier.VerifyIL("R..ctor()", @"
{
// Code size 19 (0x13)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: conv.u
IL_0003: stfld ""ref readonly int R.field""
IL_0008: ldstr ""explicit ctor""
IL_000d: call ""void System.Console.WriteLine(string)""
IL_0012: ret
}");
}

[Fact, WorkItem(63018, "https://github.com/dotnet/roslyn/issues/63018")]
public void InitRefField_AutoDefault_ReadonlyRef()
{
var source = """
using System;

var r = new R();

ref struct R
{
public readonly ref int field;

public R()
{
Console.WriteLine("explicit ctor");
}
}
""";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (7,29): warning CS0649: Field 'R.field' is never assigned to, and will always have its default value 0
// public readonly ref int field;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("R.field", "0").WithLocation(7, 29)
);

var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor"));
verifier.VerifyIL("R..ctor()", @"
{
// Code size 19 (0x13)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: conv.u
IL_0003: stfld ""ref int R.field""
IL_0008: ldstr ""explicit ctor""
IL_000d: call ""void System.Console.WriteLine(string)""
IL_0012: ret
}");
}

[Fact, WorkItem(63018, "https://github.com/dotnet/roslyn/issues/63018")]
public void InitRefField_AutoDefault_WithOtherFieldInitializer()
{
var source = """
using System;

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

ref struct R
{
public ref int field;
public int otherField = 42;

public R()
{
Console.WriteLine("explicit ctor");
}
}
""";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular11);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("explicit ctor"));
verifier.VerifyIL("R..ctor()", @"
{
// Code size 27 (0x1b)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: conv.u
IL_0003: stfld ""ref int R.field""
IL_0008: ldarg.0
IL_0009: ldc.i4.s 42
IL_000b: stfld ""int R.otherField""
IL_0010: ldstr ""explicit ctor""
IL_0015: call ""void System.Console.WriteLine(string)""
IL_001a: ret
}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3929,6 +3929,46 @@ .maxstack 2
");
}

[Fact]
public void ImplicitlyInitializedField_Pointer()
{
var source = """
using System;

_ = new R();

unsafe struct R
{
public int* field;

public R()
{
Console.WriteLine("explicit ctor");
}
}
""";
var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe);
comp.VerifyDiagnostics(
// (7,17): warning CS0649: Field 'R.field' is never assigned to, and will always have its default value
// public int* field;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("R.field", "").WithLocation(7, 17)
);
var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: "explicit ctor");
verifier.VerifyIL("R..ctor()", @"
{
// Code size 25 (0x19)
.maxstack 1
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldflda ""int* R.field""
IL_0007: initobj ""int*""
IL_000d: ldstr ""explicit ctor""
IL_0012: call ""void System.Console.WriteLine(string)""
IL_0017: nop
IL_0018: ret
}");
}

[Fact]
public void ImplicitlyInitializedField_NotOtherStruct()
{
Expand Down

0 comments on commit 6218c6a

Please sign in to comment.