Skip to content

Commit

Permalink
Merge pull request dotnet#25832 from dotnet/merges/dev15.7.x-to-master
Browse files Browse the repository at this point in the history
Merge dev15.7.x to master
  • Loading branch information
tmeschter committed Mar 30, 2018
2 parents 2ac411c + 52a6eb3 commit 2c383ed
Show file tree
Hide file tree
Showing 36 changed files with 1,008 additions and 255 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -661,11 +661,14 @@ private BoundExpression SpillArrayElementAccess(
/// l += goo(ref l);
///
/// even though l is a local, we must access it via a temp since "goo(ref l)" may change it
/// on between accesses.
/// on between accesses.
///
/// Note: In `this.x++`, `this` cannot change between reads. But in `(this, ...) == (..., this.Mutate())` it can.
/// </summary>
internal static bool CanChangeValueBetweenReads(
BoundExpression expression,
bool localsMayBeAssignedOrCaptured = true)
bool localsMayBeAssignedOrCaptured = true,
bool structThisCanChangeValueBetweenReads = false)
{
if (expression.IsDefaultValue())
{
Expand All @@ -681,6 +684,8 @@ internal static bool CanChangeValueBetweenReads(
switch (expression.Kind)
{
case BoundKind.ThisReference:
return structThisCanChangeValueBetweenReads && ((BoundThisReference)expression).Type.IsStructType();

case BoundKind.BaseReference:
return false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ private ImmutableArray<BoundExpression> InvokeDeconstructMethod(DeconstructMetho
private BoundExpression EvaluateSideEffectingArgumentToTemp(BoundExpression arg, ArrayBuilder<BoundExpression> effects,
ref ArrayBuilder<LocalSymbol> temps)
{
if (CanChangeValueBetweenReads(arg, localsMayBeAssignedOrCaptured: true))
if (CanChangeValueBetweenReads(arg, localsMayBeAssignedOrCaptured: true, structThisCanChangeValueBetweenReads: true))
{
BoundAssignmentOperator store;
var temp = _factory.StoreToTemp(arg, out store);
Expand Down
50 changes: 50 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDeconstructTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8279,5 +8279,55 @@ void M() { var (y1, y2) =}
Assert.Null(info.Method);
Assert.Empty(info.Nested);
}

[Fact]
public void TestDeconstructStructThis()
{
string source = @"
public struct S
{
int I;
public static void Main()
{
S s = new S();
s.M();
}
public void M()
{
this.I = 42;
var (x, (y, z)) = (this, this /* mutating deconstruction */);
System.Console.Write($""{x.I} {y} {z}"");
}
void Deconstruct(out int x1, out int x2) { x1 = I++; x2 = I++; }
}
";
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "42 42 43");
}

[Fact]
public void TestDeconstructClassThis()
{
string source = @"
public class C
{
int I;
public static void Main()
{
C c = new C();
c.M();
}
public void M()
{
this.I = 42;
var (x, (y, z)) = (this, this /* mutating deconstruction */);
System.Console.Write($""{x.I} {y} {z}"");
}
void Deconstruct(out int x1, out int x2) { x1 = I++; x2 = I++; }
}
";
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "44 42 43");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,215 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
[CompilerTrait(CompilerFeature.StackAllocInitializer)]
public class CodeGenStackAllocInitializerTests : CompilingTestBase
{
[Fact]
public void TestUnmanaged_Pointer()
{
var text = @"
using System;
unsafe class Test
{
static void Print<T>(T* p) where T : unmanaged
{
for (int i = 0; i < 3; i++)
Console.Write(p[i]);
}
static void M<T>(T arg) where T : unmanaged
{
var obj1 = stackalloc T[3] { arg, arg, arg };
Print<T>(obj1);
var obj2 = stackalloc T[ ] { arg, arg, arg };
Print<T>(obj2);
var obj3 = stackalloc [ ] { arg, arg, arg };
Print<T>(obj3);
}
public static void Main()
{
M(42);
}
}";
CompileAndVerify(text,
parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_3),
options: TestOptions.UnsafeReleaseExe,
expectedOutput: "424242424242424242",
verify: Verification.Fails).VerifyIL("Test.M<T>(T)",
@"{
// Code size 163 (0xa3)
.maxstack 4
IL_0000: ldc.i4.3
IL_0001: conv.u
IL_0002: sizeof ""T""
IL_0008: mul.ovf.un
IL_0009: localloc
IL_000b: dup
IL_000c: ldarg.0
IL_000d: stobj ""T""
IL_0012: dup
IL_0013: sizeof ""T""
IL_0019: add
IL_001a: ldarg.0
IL_001b: stobj ""T""
IL_0020: dup
IL_0021: ldc.i4.2
IL_0022: conv.i
IL_0023: sizeof ""T""
IL_0029: mul
IL_002a: add
IL_002b: ldarg.0
IL_002c: stobj ""T""
IL_0031: call ""void Test.Print<T>(T*)""
IL_0036: ldc.i4.3
IL_0037: conv.u
IL_0038: sizeof ""T""
IL_003e: mul.ovf.un
IL_003f: localloc
IL_0041: dup
IL_0042: ldarg.0
IL_0043: stobj ""T""
IL_0048: dup
IL_0049: sizeof ""T""
IL_004f: add
IL_0050: ldarg.0
IL_0051: stobj ""T""
IL_0056: dup
IL_0057: ldc.i4.2
IL_0058: conv.i
IL_0059: sizeof ""T""
IL_005f: mul
IL_0060: add
IL_0061: ldarg.0
IL_0062: stobj ""T""
IL_0067: call ""void Test.Print<T>(T*)""
IL_006c: ldc.i4.3
IL_006d: conv.u
IL_006e: sizeof ""T""
IL_0074: mul.ovf.un
IL_0075: localloc
IL_0077: dup
IL_0078: ldarg.0
IL_0079: stobj ""T""
IL_007e: dup
IL_007f: sizeof ""T""
IL_0085: add
IL_0086: ldarg.0
IL_0087: stobj ""T""
IL_008c: dup
IL_008d: ldc.i4.2
IL_008e: conv.i
IL_008f: sizeof ""T""
IL_0095: mul
IL_0096: add
IL_0097: ldarg.0
IL_0098: stobj ""T""
IL_009d: call ""void Test.Print<T>(T*)""
IL_00a2: ret
}");
}

[Fact]
public void TestUnmanaged_Span()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public void M<T>(T arg) where T : unmanaged
{
Span<T> obj1 = stackalloc T[3] { arg, arg, arg };
Span<T> obj2 = stackalloc T[ ] { arg, arg, arg };
Span<T> obj3 = stackalloc [ ] { arg, arg, arg };
}
}
", options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_3));

CompileAndVerify(comp, verify: Verification.Fails).VerifyIL("Test.M<T>(T)",
@"{
// Code size 175 (0xaf)
.maxstack 4
.locals init (int V_0)
IL_0000: ldc.i4.3
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: conv.u
IL_0004: sizeof ""T""
IL_000a: mul.ovf.un
IL_000b: localloc
IL_000d: dup
IL_000e: ldarg.1
IL_000f: stobj ""T""
IL_0014: dup
IL_0015: sizeof ""T""
IL_001b: add
IL_001c: ldarg.1
IL_001d: stobj ""T""
IL_0022: dup
IL_0023: ldc.i4.2
IL_0024: conv.i
IL_0025: sizeof ""T""
IL_002b: mul
IL_002c: add
IL_002d: ldarg.1
IL_002e: stobj ""T""
IL_0033: ldloc.0
IL_0034: newobj ""System.Span<T>..ctor(void*, int)""
IL_0039: pop
IL_003a: ldc.i4.3
IL_003b: stloc.0
IL_003c: ldloc.0
IL_003d: conv.u
IL_003e: sizeof ""T""
IL_0044: mul.ovf.un
IL_0045: localloc
IL_0047: dup
IL_0048: ldarg.1
IL_0049: stobj ""T""
IL_004e: dup
IL_004f: sizeof ""T""
IL_0055: add
IL_0056: ldarg.1
IL_0057: stobj ""T""
IL_005c: dup
IL_005d: ldc.i4.2
IL_005e: conv.i
IL_005f: sizeof ""T""
IL_0065: mul
IL_0066: add
IL_0067: ldarg.1
IL_0068: stobj ""T""
IL_006d: ldloc.0
IL_006e: newobj ""System.Span<T>..ctor(void*, int)""
IL_0073: pop
IL_0074: ldc.i4.3
IL_0075: stloc.0
IL_0076: ldloc.0
IL_0077: conv.u
IL_0078: sizeof ""T""
IL_007e: mul.ovf.un
IL_007f: localloc
IL_0081: dup
IL_0082: ldarg.1
IL_0083: stobj ""T""
IL_0088: dup
IL_0089: sizeof ""T""
IL_008f: add
IL_0090: ldarg.1
IL_0091: stobj ""T""
IL_0096: dup
IL_0097: ldc.i4.2
IL_0098: conv.i
IL_0099: sizeof ""T""
IL_009f: mul
IL_00a0: add
IL_00a1: ldarg.1
IL_00a2: stobj ""T""
IL_00a7: ldloc.0
IL_00a8: newobj ""System.Span<T>..ctor(void*, int)""
IL_00ad: pop
IL_00ae: ret
}");
}

[Fact]
public void TestLambdaCapture()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -648,13 +648,43 @@ S Mutate()
public static implicit operator S(int value) { return new S() { I = value }; }
public static bool operator==(S s1, S s2) { System.Console.Write($""{s1.I} == {s2.I}, ""); return s1.I == s2.I; }
public static bool operator!=(S s1, S s2) { throw null; }
public override bool Equals(object o) { throw null; }
public override int GetHashCode() { throw null; }
}";
var comp = CompileAndVerify(source, expectedOutput: "1 == 1, 2 == 2, True");
comp.VerifyDiagnostics();
}

// https://github.com/dotnet/roslyn/issues/25488
// We need to create a temp for `this`, otherwise it gets mutated
[Fact]
public void TestThisClass()
{
var source = @"
public class C
{
public int I;
public static void Main()
{
C c = new C() { I = 1 };
c.M();
}
void M()
{
System.Console.Write((this, 2) == (2, this.Mutate()));
}
var comp = CompileAndVerify(source, expectedOutput: "2 == 1, False");
//comp.VerifyDiagnostics();
C Mutate()
{
I++;
return this;
}
public static implicit operator C(int value) { return new C() { I = value }; }
public static bool operator==(C c1, C c2) { System.Console.Write($""{c1.I} == {c2.I}, ""); return c1.I == c2.I; }
public static bool operator!=(C c1, C c2) { throw null; }
public override bool Equals(object o) { throw null; }
public override int GetHashCode() { throw null; }
}";
var comp = CompileAndVerify(source, expectedOutput: "2 == 2, 2 == 2, True");
comp.VerifyDiagnostics();
}

[Fact]
Expand Down
Loading

0 comments on commit 2c383ed

Please sign in to comment.