Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EnC - Allow lambda parameters to be changed to discards #52107

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -750,9 +750,9 @@ private bool AreNamespacesEqual(NamespaceSymbol @namespace, NamespaceSymbol othe

private bool AreParametersEqual(ParameterSymbol parameter, ParameterSymbol other)
{
// We allow parameters to be renamed, so don't check Identifier
Debug.Assert(parameter.Ordinal == other.Ordinal);
return StringOrdinalComparer.Equals(parameter.MetadataName, other.MetadataName) &&
(parameter.RefKind == other.RefKind) &&
return (parameter.RefKind == other.RefKind) &&
_comparer.Equals(parameter.Type, other.Type);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,188 @@ static void Main() { }
Handle(2, TableIndex.AssemblyRef));
}

[Fact]
public void ModifyMethod_RenameParameter()
{
var source0 =
@"class C
{
static void Main() { }
static string F(int a) { return a.ToString(); }
}";
var source1 =
@"class C
{
static void Main() { }
static string F(int x) { return x.ToString(); }
}";
var source2 =
@"class C
{
static void Main() { }
static string F(int b) { return b.ToString(); }
}";

var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe);
var compilation1 = compilation0.WithSource(source1);
var compilation2 = compilation1.WithSource(source2);

var method0 = compilation0.GetMember<MethodSymbol>("C.F");

// Verify full metadata contains expected rows.
var bytes0 = compilation0.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb));
using var md0 = ModuleMetadata.CreateFromImage(bytes0);
var reader0 = md0.MetadataReader;

CheckNames(reader0, reader0.GetTypeDefNames(), "<Module>", "C");
CheckNames(reader0, reader0.GetMethodDefNames(), "Main", "F", ".ctor");
CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", "ToString", /*DebuggableAttribute*/".ctor");
CheckNames(reader0, reader0.GetParameterDefNames(), "a");

var generation0 = EmitBaseline.CreateInitialBaseline(
md0,
EmptyLocalsProvider);
var method1 = compilation1.GetMember<MethodSymbol>("C.F");

var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method0, method1, options: SemanticEditOption.EmitAllParametersForMethodUpdate)));

// Verify delta metadata contains expected rows.
using var md1 = diff1.GetMetadata();
var reader1 = md1.Reader;
var readers = new[] { reader0, reader1 };

EncValidation.VerifyModuleMvid(1, reader0, reader1);

var x = Visualize(generation0.OriginalMetadata, md1);

CheckNames(readers, reader1.GetTypeDefNames());
CheckNames(readers, reader1.GetMethodDefNames(), "F");
CheckNames(readers, reader1.GetMemberRefNames(), "ToString");
CheckNames(readers, reader1.GetParameterDefNames(), "x");
// CheckNames(readers, reader1.GetParameterDefNames(MetadataTokens.MethodDefinitionHandle(1)), "x");

CheckEncLog(reader1,
Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(7, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
//Row(2, TableIndex.MethodDef, EditAndContinueOperation.AddParameter),
Row(1, TableIndex.Param, EditAndContinueOperation.Default));

CheckEncMap(reader1,
Handle(7, TableIndex.TypeRef),
Handle(8, TableIndex.TypeRef),
Handle(2, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(6, TableIndex.MemberRef),
Handle(2, TableIndex.StandAloneSig),
Handle(2, TableIndex.AssemblyRef));

var method2 = compilation2.GetMember<MethodSymbol>("C.F");

var diff2 = compilation2.EmitDifference(
diff1.NextGeneration,
ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method1, method2, options: SemanticEditOption.EmitAllParametersForMethodUpdate)));

// Verify delta metadata contains expected rows.
using var md2 = diff2.GetMetadata();
var reader2 = md2.Reader;
readers = new[] { reader0, reader1, reader2 };

CheckNames(readers, reader2.GetTypeDefNames());
CheckNames(readers, reader2.GetMethodDefNames(), "F");
CheckNames(readers, reader2.GetMemberRefNames(), "ToString");
CheckNames(readers, reader2.GetParameterDefNames(), "b");
//CheckNames(readers, reader2.GetParameterDefNames(MetadataTokens.MethodDefinitionHandle(1)), "b");

CheckEncLog(reader2,
Row(3, TableIndex.AssemblyRef, EditAndContinueOperation.Default),
Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default),
Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default),
Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
//Row(2, TableIndex.MethodDef, EditAndContinueOperation.AddParameter),
Row(1, TableIndex.Param, EditAndContinueOperation.Default));

CheckEncMap(reader2,
Handle(9, TableIndex.TypeRef),
Handle(10, TableIndex.TypeRef),
Handle(2, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(7, TableIndex.MemberRef),
Handle(3, TableIndex.StandAloneSig),
Handle(3, TableIndex.AssemblyRef));

var v0 = CompileAndVerify(compilation0);

// Before
v0.VerifyIL("C.F", @"
{
// Code size 13 (0xd)
.maxstack 1
.locals init (string V_0)
IL_0000: nop
IL_0001: ldarga.s V_0
IL_0003: call ""string int.ToString()""
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
}
");

v0.VerifyPdb("C.F", @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<entryPoint declaringType=""C"" methodName=""Main"" />
<methods>
<method containingType=""C"" name=""F"" parameterNames=""a"">
<customDebugInfo>
<forward declaringType=""C"" methodName=""Main"" />
<encLocalSlotMap>
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""28"" endLine=""4"" endColumn=""29"" document=""1"" />
<entry offset=""0x1"" startLine=""4"" startColumn=""30"" endLine=""4"" endColumn=""50"" document=""1"" />
<entry offset=""0xb"" startLine=""4"" startColumn=""51"" endLine=""4"" endColumn=""52"" document=""1"" />
</sequencePoints>
</method>
</methods>
</symbols>
");

diff1.VerifyPdb(new[] { 0x06000002 }, @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method token=""0x6000002"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""28"" endLine=""4"" endColumn=""29"" document=""1"" />
<entry offset=""0x1"" startLine=""4"" startColumn=""30"" endLine=""4"" endColumn=""50"" document=""1"" />
<entry offset=""0xb"" startLine=""4"" startColumn=""51"" endLine=""4"" endColumn=""52"" document=""1"" />
</sequencePoints>
</method>
</methods>
</symbols>
");
}

[CompilerTrait(CompilerFeature.Tuples)]
[Fact]
public void ModifyMethod_WithTuples()
Expand Down Expand Up @@ -11327,7 +11509,7 @@ namespace N
{
record R(int X)
{
protected virtual bool PrintMembers(System.Text.StringBuilder builder)
protected virtual bool PrintMembers(System.Text.StringBuilder sb)
{
return true;
}
Expand Down Expand Up @@ -11421,7 +11603,7 @@ namespace N
{
record R(int X)
{
protected virtual bool PrintMembers(System.Text.StringBuilder builder)
protected virtual bool PrintMembers(System.Text.StringBuilder sb)
{
return true;
}
Expand Down Expand Up @@ -11527,5 +11709,89 @@ public void TopLevelStatement_Update()
Handle(7, TableIndex.MemberRef),
Handle(2, TableIndex.AssemblyRef));
}

[Fact]
public void LambdaParameterToDiscard()
{
var source0 = MarkedSource(@"
using System;
class C
{
void M()
{
var x = new Func<int, int, int>(<N:0>(a, b) => a + b + 1</N:0>);
Console.WriteLine(x(1, 2));
}
}");
var source1 = MarkedSource(@"
using System;
class C
{
void M()
{
var x = new Func<int, int, int>(<N:0>(_, _) => 10</N:0>);
Console.WriteLine(x(1, 2));
}
}");

var compilation0 = CreateCompilation(source0.Tree, options: ComSafeDebugDll);
var compilation1 = compilation0.WithSource(source1.Tree);

var method0 = compilation0.GetMember<MethodSymbol>("C.M");
var method1 = compilation1.GetMember<MethodSymbol>("C.M");

var v0 = CompileAndVerify(compilation0, verify: Verification.Skipped);

using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);

var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo);

var diff = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(
SemanticEdit.Create(SemanticEditKind.Update, method0, method1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));

// There should be no diagnostics from rude edits
diff.EmitResult.Diagnostics.Verify();

diff.VerifySynthesizedMembers(
"C: {<>c}",
"C.<>c: {<>9__0_0, <M>b__0_0}");

diff.VerifyIL("C.M",
@"
{
// Code size 48 (0x30)
.maxstack 3
.locals init ([unchanged] V_0,
System.Func<int, int, int> V_1) //x
IL_0000: nop
IL_0001: ldsfld ""System.Func<int, int, int> C.<>c.<>9__0_0""
IL_0006: dup
IL_0007: brtrue.s IL_0020
IL_0009: pop
IL_000a: ldsfld ""C.<>c C.<>c.<>9""
IL_000f: ldftn ""int C.<>c.<M>b__0_0(int, int)""
IL_0015: newobj ""System.Func<int, int, int>..ctor(object, System.IntPtr)""
IL_001a: dup
IL_001b: stsfld ""System.Func<int, int, int> C.<>c.<>9__0_0""
IL_0020: stloc.1
IL_0021: ldloc.1
IL_0022: ldc.i4.1
IL_0023: ldc.i4.2
IL_0024: callvirt ""int System.Func<int, int, int>.Invoke(int, int)""
IL_0029: call ""void System.Console.WriteLine(int)""
IL_002e: nop
IL_002f: ret
}");

diff.VerifyIL("C.<>c.<M>b__0_0(int, int)", @"
{
// Code size 3 (0x3)
.maxstack 1
IL_0000: ldc.i4.s 10
IL_0002: ret
}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,68 @@ class C
Assert.NotNull(other);
}

[Fact]
public void Method_ParameterRename()
{
var source0 = @"
using System.Collections.Generic;
class C
{
string M(string s) => s.ToString();
}";
var source1 = @"
using System.Collections.Generic;
class C
{
string M(string m) => m.ToString();
}";
var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
var compilation1 = compilation0.WithSource(source1);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

var member = compilation1.GetMember<MethodSymbol>("C.M");
var other = matcher.MapDefinition(member.GetCciAdapter());
Assert.NotNull(other);
}

[Fact]
public void Method_ParameterRenameToDiscard()
{
var source0 = @"
using System.Collections.Generic;
class C
{
string M(string s) => s.ToString();
}";
var source1 = @"
using System.Collections.Generic;
class C
{
string M(string _) => ""Hello"";
}";
var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
var compilation1 = compilation0.WithSource(source1);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

var member = compilation1.GetMember<MethodSymbol>("C.M");
var other = matcher.MapDefinition(member.GetCciAdapter());
Assert.NotNull(other);
}

[Fact]
public void Field_NullableChange()
{
Expand Down Expand Up @@ -1443,7 +1505,7 @@ public record R
var source1 = @"
public record R
{
protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;
protected virtual bool PrintMembers(System.Text.StringBuilder sb) => true;
}";
var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll);
var compilation1 = compilation0.WithSource(source1);
Expand All @@ -1468,7 +1530,7 @@ public void Record_RemoveSynthesizedMember_PrintMembers()
var source0 = @"
public record R
{
protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;
protected virtual bool PrintMembers(System.Text.StringBuilder sb) => true;
}";
var source1 = @"
public record R
Expand Down
Loading