Skip to content

Commit

Permalink
Check val escape for struct field initializers
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Apr 5, 2022
1 parent 4f4a757 commit cbe611c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,17 @@ internal static void BindRegularCSharpFieldInitializers(
parentBinder = parentBinder.GetFieldInitializerBinder(fieldSymbol);

BoundFieldEqualsValue boundInitializer = BindFieldInitializer(parentBinder, fieldSymbol, initializerNode, diagnostics);

if (!boundInitializer.Value.HasAnyErrors)
{
var field = boundInitializer.Field;
if (field.Type.IsRefLikeType)
{
var value = parentBinder.ValidateEscape(boundInitializer.Value, ExternalScope, isByRef: false, diagnostics);
boundInitializer = boundInitializer.Update(field, boundInitializer.Locals, value);
}
}

boundInitializers.Add(boundInitializer);
break;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3257,5 +3257,80 @@ static void M(I i)
// var s1 = i.F1();
Diagnostic(ErrorCode.ERR_InteropStructContainsMethods, "i.F1()").WithArguments("S1").WithLocation(6, 18));
}

[WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")]
[Fact]
public void FieldInitializer_EscapeAnalysis_01()
{
var source =
@"using System;
ref struct Example
{
public Span<byte> Field = stackalloc byte[512];
public Span<byte> Property { get; } = stackalloc byte[512];
public Example() {}
}";
var comp = CreateCompilationWithSpan(source);
comp.VerifyDiagnostics(
// (5,31): error CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method
// public Span<byte> Field = stackalloc byte[512];
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc byte[512]").WithArguments("System.Span<byte>").WithLocation(5, 31),
// (6,43): error CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method
// public Span<byte> Property { get; } = stackalloc byte[512];
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc byte[512]").WithArguments("System.Span<byte>").WithLocation(6, 43));
}

[WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")]
[Fact]
public void FieldInitializer_EscapeAnalysis_02()
{
var source =
@"using System;
ref struct Example
{
public Span<byte> Field;
}
ref struct E2
{
public Span<byte> Field = new Example { Field = stackalloc byte[512] }.Field;
public Span<byte> Property { get; } = new Example { Field = stackalloc byte[512] }.Field;
public E2() { }
}";
var comp = CreateCompilationWithSpan(source);
comp.VerifyDiagnostics(
// (8,45): error CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method
// public Span<byte> Field = new Example { Field = stackalloc byte[512] }.Field;
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "Field = stackalloc byte[512]").WithArguments("System.Span<byte>").WithLocation(8, 45),
// (9,57): error CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method
// public Span<byte> Property { get; } = new Example { Field = stackalloc byte[512] }.Field;
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "Field = stackalloc byte[512]").WithArguments("System.Span<byte>").WithLocation(9, 57));
}

[Fact]
public void FieldInitializer_EscapeAnalysis_03()
{
var source =
@"using System;
delegate Span<T> D<T>();
ref struct Example
{
public Span<byte> Field = F(() => stackalloc byte[512]);
public Span<byte> Property { get; } = F(() => stackalloc byte[512]);
public Example() {}
static Span<T> F<T>(D<T> d) => d();
}";
var comp = CreateCompilationWithSpan(source);
comp.VerifyDiagnostics(
// (5,31): error CS0188: The 'this' object cannot be used before all of its fields have been assigned
// public Span<byte> Field = F(() => stackalloc byte[512]);
Diagnostic(ErrorCode.ERR_UseDefViolationThis, "F").WithArguments("this").WithLocation(5, 31),
// (5,39): error CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method
// public Span<byte> Field = F(() => stackalloc byte[512]);
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc byte[512]").WithArguments("System.Span<byte>").WithLocation(5, 39),
// (6,51): error CS8353: A result of a stackalloc expression of type 'Span<byte>' cannot be used in this context because it may be exposed outside of the containing method
// public Span<byte> Property { get; } = F(() => stackalloc byte[512]);
Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc byte[512]").WithArguments("System.Span<byte>").WithLocation(6, 51));
}
}
}

0 comments on commit cbe611c

Please sign in to comment.