Skip to content

Commit

Permalink
Merge pull request #28692 from dotnet/merges/dev15.9.x-to-dev15.9.x-v…
Browse files Browse the repository at this point in the history
…s-deps

Merge dev15.9.x to dev15.9.x-vs-deps
  • Loading branch information
dpoeschl committed Jul 19, 2018
2 parents 65ae3fb + 69fb90a commit 03b68dc
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 17 deletions.
16 changes: 14 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Expand Up @@ -6153,6 +6153,16 @@ private static void CombineExtensionMethodArguments(BoundExpression receiver, An

if (!hasError && fieldSymbol.IsFixed && !IsInsideNameof)
{
// SPEC: In a member access of the form E.I, if E is of a struct type and a member lookup of I in
// that struct type identifies a fixed size member, then E.I is evaluated an classified as follows:
// * If the expression E.I does not occur in an unsafe context, a compile-time error occurs.
// * If E is classified as a value, a compile-time error occurs.
// * Otherwise, if E is a moveable variable and the expression E.I is not a fixed_pointer_initializer,
// a compile-time error occurs.
// * Otherwise, E references a fixed variable and the result of the expression is a pointer to the
// first element of the fixed size buffer member I in E. The result is of type S*, where S is
// the element type of I, and is classified as a value.

TypeSymbol receiverType = receiver.Type;

// Reflect errors that have been reported elsewhere...
Expand All @@ -6161,11 +6171,13 @@ private static void CombineExtensionMethodArguments(BoundExpression receiver, An
if (!hasError)
{
var isFixedStatementExpression = SyntaxFacts.IsFixedStatementExpression(node);
Symbol accessedLocalOrParameterOpt;
if (ExpressionRequiresFixing(receiver, out accessedLocalOrParameterOpt) != isFixedStatementExpression)

if (IsMoveableVariable(receiver, out Symbol accessedLocalOrParameterOpt) != isFixedStatementExpression)
{
if (indexed)
{
// SPEC C# 7.3: If the fixed size buffer access is the receiver of an element_access_expression,
// E may be either fixed or moveable
CheckFeatureAvailability(node, MessageID.IDS_FeatureIndexingMovableFixedBuffers, diagnostics);
}
else
Expand Down
31 changes: 17 additions & 14 deletions src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Expand Up @@ -2120,7 +2120,7 @@ private BoundExpression BindAddressOfExpression(PrefixUnaryExpressionSyntax node
if (!hasErrors)
{
Symbol accessedLocalOrParameterOpt;
if (ExpressionRequiresFixing(operand, out accessedLocalOrParameterOpt) != isFixedStatementAddressOfExpression)
if (IsMoveableVariable(operand, out accessedLocalOrParameterOpt) != isFixedStatementAddressOfExpression)
{
Error(diagnostics, isFixedStatementAddressOfExpression ? ErrorCode.ERR_FixedNotNeeded : ErrorCode.ERR_FixedNeeded, node);
hasErrors = true;
Expand All @@ -2135,9 +2135,13 @@ private BoundExpression BindAddressOfExpression(PrefixUnaryExpressionSyntax node
return new BoundAddressOfOperator(node, operand, pointerType, hasErrors);
}

// Basically a port of ExpressionBinder::isFixedExpression, which basically implements spec section 18.3.
// NOTE: internal to allow using in tests.
internal bool ExpressionRequiresFixing(BoundExpression expr, out Symbol accessedLocalOrParameterOpt)
/// <summary>
/// Checks to see whether an expression is a "moveable" variable according to the spec. Moveable
/// variables have underlying memory which may be moved by the runtime. The spec defines anything
/// not fixed as moveable and specifies the expressions which are fixed.
/// </summary>

internal bool IsMoveableVariable(BoundExpression expr, out Symbol accessedLocalOrParameterOpt)
{
accessedLocalOrParameterOpt = null;

Expand Down Expand Up @@ -2233,22 +2237,21 @@ internal bool ExpressionRequiresFixing(BoundExpression expr, out Symbol accessed
}
case BoundKind.PointerElementAccess:
{
// When using pointers in unsafe code, it is generally left for the user to make sure memory is fixed/pinned correctly.
// Except for fixed fields, we want to produce appropriate diagnostics if field is not fixed before access.
// C# 7.3:
// a variable resulting from a... pointer_element_access of the form P[E] [is fixed] if P
// is not a fixed size buffer expression, or if the expression is a fixed size buffer
// member_access of the form E.I and E is a fixed variable
BoundExpression underlyingExpr = ((BoundPointerElementAccess)expr).Expression;
if (underlyingExpr.Kind == BoundKind.FieldAccess)
if (underlyingExpr is BoundFieldAccess fieldAccess && fieldAccess.FieldSymbol.IsFixed)
{
FieldSymbol field = ((BoundFieldAccess)underlyingExpr).FieldSymbol;
if (field.IsFixed)
{
return true;
}
expr = fieldAccess.ReceiverOpt;
continue;
}

return false;
}
case BoundKind.PropertyAccess: // Never a variable.
case BoundKind.IndexerAccess: // Never a variable.
case BoundKind.PropertyAccess: // Never fixed
case BoundKind.IndexerAccess: // Never fixed
default:
{
return true;
Expand Down
67 changes: 67 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/FixedSizeBufferTests.cs
Expand Up @@ -1067,5 +1067,72 @@ unsafe struct Foo
// public int* M2 => &Bar[1];
Diagnostic(ErrorCode.ERR_FixedNeeded, "&Bar[1]").WithLocation(7, 23));
}

[Fact]
public void StaticField()
{
var verifier = CompileAndVerify(@"
unsafe struct S
{
public fixed int Buf[1];
}
unsafe class C
{
static S s_f;
public void M()
{
s_f.Buf[0] = 1;
}
}", options: TestOptions.UnsafeReleaseDll);
verifier.VerifyIL("C.M", @"
{
// Code size 18 (0x12)
.maxstack 2
IL_0000: ldsflda ""S C.s_f""
IL_0005: ldflda ""int* S.Buf""
IL_000a: ldflda ""int S.<Buf>e__FixedBuffer.FixedElementField""
IL_000f: ldc.i4.1
IL_0010: stind.i4
IL_0011: ret
}");
}

[Fact]
public void FixedSizeBufferOffPointerAcess()
{
var verifier = CompileAndVerify(@"
using System;
unsafe struct S
{
public fixed int Buf[1];
}
unsafe class C
{
public void M(IntPtr ptr)
{
S* s = (S*)ptr;
int* x = s->Buf;
int* y = &s->Buf[0];
}
}", options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails);
verifier.VerifyIL("C.M", @"
{
// Code size 32 (0x20)
.maxstack 1
.locals init (S* V_0) //s
IL_0000: ldarg.1
IL_0001: call ""void* System.IntPtr.op_Explicit(System.IntPtr)""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldflda ""int* S.Buf""
IL_000d: ldflda ""int S.<Buf>e__FixedBuffer.FixedElementField""
IL_0012: pop
IL_0013: ldloc.0
IL_0014: ldflda ""int* S.Buf""
IL_0019: ldflda ""int S.<Buf>e__FixedBuffer.FixedElementField""
IL_001e: pop
IL_001f: ret
}");
}
}
}
120 changes: 119 additions & 1 deletion src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs
Expand Up @@ -2308,7 +2308,7 @@ public override BoundNode Visit(BoundNode node)
{
text = SymbolDisplay.FormatLiteral(text, quote: false);

if (_binder.ExpressionRequiresFixing(expr, out Symbol accessedLocalOrParameterOpt))
if (_binder.IsMoveableVariable(expr, out Symbol accessedLocalOrParameterOpt))
{
_builder.Add($"Yes, {expr.Kind} '{text}' requires fixing.");
}
Expand Down Expand Up @@ -8690,5 +8690,123 @@ unsafe void M(in int p)
}

#endregion

[Fact]
public void AddressOfFixedSizeBuffer()
{
CreateCompilation(@"
unsafe struct S
{
public fixed int Buf[1];
}
unsafe class C
{
static S s_f;
void M(S s)
{
fixed (int* a = &s.Buf) {}
fixed (int* b = &s_f.Buf) {}
int* c = &s.Buf;
int* d = &s_f.Buf;
}
}", options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(
// (12,25): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression
// fixed (int* a = &s.Buf) {}
Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&s.Buf").WithLocation(12, 25),
// (13,26): error CS1666: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement.
// fixed (int* b = &s_f.Buf) {}
Diagnostic(ErrorCode.ERR_FixedBufferNotFixed, "s_f.Buf").WithLocation(13, 26),
// (14,18): error CS0266: Cannot implicitly convert type 'int**' to 'int*'. An explicit conversion exists (are you missing a cast?)
// int* c = &s.Buf;
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "&s.Buf").WithArguments("int**", "int*").WithLocation(14, 18),
// (15,19): error CS1666: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement.
// int* d = &s_f.Buf;
Diagnostic(ErrorCode.ERR_FixedBufferNotFixed, "s_f.Buf").WithLocation(15, 19));
}

[Fact]
public void FixedFixedSizeBuffer()
{
CreateCompilation(@"
unsafe struct S
{
public fixed int Buf[1];
}
unsafe class C
{
static S s_f;
void M(S s)
{
fixed (int* a = s.Buf) {}
fixed (int* b = s_f.Buf) {}
int* c = s.Buf;
int* d = s_f.Buf;
}
}", options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(
// (12,25): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression
// fixed (int* a = s.Buf) {}
Diagnostic(ErrorCode.ERR_FixedNotNeeded, "s.Buf").WithLocation(12, 25),
// (15,18): error CS1666: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement.
// int* d = s_f.Buf;
Diagnostic(ErrorCode.ERR_FixedBufferNotFixed, "s_f.Buf").WithLocation(15, 18));
}

[Fact]
public void NoPointerDerefMoveableFixedSizeBuffer()
{
CreateCompilation(@"
unsafe struct S
{
public fixed int Buf[1];
}
unsafe class C
{
static S s_f;
void M(S s)
{
int x = *s.Buf;
int y = *s_f.Buf;
}
}", options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(
// (13,18): error CS1666: You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement.
// int y = *s_f.Buf;
Diagnostic(ErrorCode.ERR_FixedBufferNotFixed, "s_f.Buf").WithLocation(13, 18));
}

[Fact]
public void AddressOfElementAccessFixedSizeBuffer()
{
CreateCompilation(@"
unsafe struct S
{
public fixed int Buf[1];
}
unsafe class C
{
static S s_f;
void M(S s)
{
fixed (int* a = &s.Buf[0]) { }
fixed (int* b = &s_f.Buf[0]) { }
int* c = &s.Buf[0];
int* d = &s_f.Buf[0];
int* e = &(s.Buf[0]);
int* f = &(s_f.Buf[0]);
}
}", options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(
// (12,25): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression
// fixed (int* a = &s.Buf[0]) { }
Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&s.Buf[0]").WithLocation(12, 25),
// (15,18): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer
// int* d = &s_f.Buf[0];
Diagnostic(ErrorCode.ERR_FixedNeeded, "&s_f.Buf[0]").WithLocation(15, 18),
// (17,18): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer
// int* f = &(s_f.Buf[0]);
Diagnostic(ErrorCode.ERR_FixedNeeded, "&(s_f.Buf[0])").WithLocation(17, 18));
}
}
}

0 comments on commit 03b68dc

Please sign in to comment.