提交 6ec8f3ab 编写于 作者: A AlekseyTs

Add support for deconstruction into field-like events.

Fixes #16962.
上级 773f3c91
......@@ -18,8 +18,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used)
{
var right = node.Right;
var loweredRight = VisitExpression(right);
var loweredRight = VisitExpression(node.Right);
BoundExpression left = node.Left;
BoundExpression loweredLeft;
......@@ -39,7 +38,7 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
if (eventAccess.EventSymbol.IsWindowsRuntimeEvent)
{
Debug.Assert(node.RefKind == RefKind.None);
return VisitWindowsRuntimeEventFieldAssignmentOperator(node.Syntax, eventAccess, right);
return VisitWindowsRuntimeEventFieldAssignmentOperator(node.Syntax, eventAccess, loweredRight);
}
goto default;
}
......@@ -105,6 +104,26 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
isCompoundAssignment,
isChecked).ToExpression();
case BoundKind.EventAccess:
var eventAccess = (BoundEventAccess)rewrittenLeft;
Debug.Assert(eventAccess.IsUsableAsField);
if (eventAccess.EventSymbol.IsWindowsRuntimeEvent)
{
const bool isDynamic = false;
return RewriteWindowsRuntimeEventAssignmentOperator(eventAccess.Syntax,
eventAccess.EventSymbol,
EventAssignmentKind.Assignment,
isDynamic,
eventAccess.ReceiverOpt,
rewrittenRight);
}
// Only Windows Runtime field-like events can come through here:
// - Assignment operation is not supported for custom (non-field like) events.
// - Access to regular field-like events is expected to be lowered to at least a field access
// when we reach here.
throw ExceptionUtilities.Unreachable;
default:
return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, RefKind.None, type, used);
}
......
......@@ -120,8 +120,10 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO
return result;
}
private BoundPropertyAccess TransformPropertyAccess(BoundPropertyAccess prop, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
private BoundExpression TransformPropertyOrEventReceiver(Symbol propertyOrEvent, BoundExpression receiverOpt, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
{
Debug.Assert(propertyOrEvent.Kind == SymbolKind.Property || propertyOrEvent.Kind == SymbolKind.Event);
// We need to stash away the receiver so that it does not get evaluated twice.
// If the receiver is classified as a value of reference type then we can simply say
//
......@@ -141,14 +143,14 @@ private BoundPropertyAccess TransformPropertyAccess(BoundPropertyAccess prop, Ar
// assume that was the case.
// If the property is static or if the receiver is of kind "Base" or "this", then we can just generate prop = prop + value
if (prop.ReceiverOpt == null || prop.PropertySymbol.IsStatic || !CanChangeValueBetweenReads(prop.ReceiverOpt))
if (receiverOpt == null || propertyOrEvent.IsStatic || !CanChangeValueBetweenReads(receiverOpt))
{
return prop;
return receiverOpt;
}
Debug.Assert(prop.ReceiverOpt.Kind != BoundKind.TypeExpression);
Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression);
BoundExpression rewrittenReceiver = VisitExpression(prop.ReceiverOpt);
BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);
BoundAssignmentOperator assignmentToTemp;
......@@ -165,9 +167,7 @@ private BoundPropertyAccess TransformPropertyAccess(BoundPropertyAccess prop, Ar
stores.Add(assignmentToTemp);
temps.Add(receiverTemp.LocalSymbol);
// CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
// Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of properties.
return new BoundPropertyAccess(prop.Syntax, receiverTemp, prop.PropertySymbol, prop.ResultKind, prop.Type);
return receiverTemp;
}
private BoundDynamicMemberAccess TransformDynamicMemberAccess(BoundDynamicMemberAccess memberAccess, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
......@@ -318,8 +318,7 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce
argumentRefKinds = GetRefKindsOrNull(refKinds);
refKinds.Free();
// CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
// Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers.
// This is a temporary object that will be rewritten away before the lowering completes.
return new BoundIndexerAccess(
syntax,
transformedReceiver,
......@@ -332,15 +331,31 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce
indexerAccess.Type);
}
private BoundFieldAccess TransformReferenceTypeFieldAccess(BoundFieldAccess fieldAccess, BoundExpression receiver, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
/// <summary>
/// Returns true if the <paramref name="receiver"/> was lowered and transformed.
/// The <paramref name="receiver"/> is not changed if this function returns false.
/// </summary>
private bool TransformCompoundAssignmentFieldOrEventAccessReceiver(Symbol fieldOrEvent, ref BoundExpression receiver, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
{
Debug.Assert(fieldOrEvent.Kind == SymbolKind.Field || fieldOrEvent.Kind == SymbolKind.Event);
//If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
if (fieldOrEvent.IsStatic || !CanChangeValueBetweenReads(receiver))
{
return true;
}
else if (!receiver.Type.IsReferenceType)
{
return false;
}
Debug.Assert(receiver.Type.IsReferenceType);
Debug.Assert(receiver.Kind != BoundKind.TypeExpression);
BoundExpression rewrittenReceiver = VisitExpression(receiver);
if (rewrittenReceiver.Type.IsTypeParameter())
{
var memberContainingType = fieldAccess.FieldSymbol.ContainingType;
var memberContainingType = fieldOrEvent.ContainingType;
// From the verifier prospective type parameters do not contain fields or methods.
// the instance must be "boxed" to access the field
......@@ -352,7 +367,8 @@ private BoundFieldAccess TransformReferenceTypeFieldAccess(BoundFieldAccess fiel
var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
stores.Add(assignmentToTemp);
temps.Add(receiverTemp.LocalSymbol);
return new BoundFieldAccess(fieldAccess.Syntax, receiverTemp, fieldAccess.FieldSymbol, null);
receiver = receiverTemp;
return true;
}
private BoundDynamicIndexerAccess TransformDynamicIndexerAccess(BoundDynamicIndexerAccess indexerAccess, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
......@@ -452,7 +468,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
var propertyAccess = (BoundPropertyAccess)originalLHS;
if (propertyAccess.PropertySymbol.RefKind == RefKind.None)
{
return TransformPropertyAccess(propertyAccess, stores, temps);
// This is a temporary object that will be rewritten away before the lowering completes.
return propertyAccess.Update(TransformPropertyOrEventReceiver(propertyAccess.PropertySymbol, propertyAccess.ReceiverOpt, stores, temps),
propertyAccess.PropertySymbol, propertyAccess.ResultKind, propertyAccess.Type);
}
}
break;
......@@ -479,14 +497,9 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
var fieldAccess = (BoundFieldAccess)originalLHS;
BoundExpression receiverOpt = fieldAccess.ReceiverOpt;
//If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
if (fieldAccess.FieldSymbol.IsStatic || !CanChangeValueBetweenReads(receiverOpt))
{
return fieldAccess;
}
else if (receiverOpt.Type.IsReferenceType)
if (TransformCompoundAssignmentFieldOrEventAccessReceiver(fieldAccess.FieldSymbol, ref receiverOpt, stores, temps))
{
return TransformReferenceTypeFieldAccess(fieldAccess, receiverOpt, stores, temps);
return MakeFieldAccess(fieldAccess.Syntax, receiverOpt, fieldAccess.FieldSymbol, fieldAccess.ConstantValueOpt, fieldAccess.ResultKind, fieldAccess.Type, fieldAccess);
}
}
break;
......@@ -544,6 +557,26 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL
case BoundKind.RefValueOperator:
break;
case BoundKind.EventAccess:
{
var eventAccess = (BoundEventAccess)originalLHS;
Debug.Assert(eventAccess.IsUsableAsField);
BoundExpression receiverOpt = eventAccess.ReceiverOpt;
if (eventAccess.EventSymbol.IsWindowsRuntimeEvent)
{
// This is a temporary object that will be rewritten away before the lowering completes.
return eventAccess.Update(TransformPropertyOrEventReceiver(eventAccess.EventSymbol, eventAccess.ReceiverOpt, stores, temps),
eventAccess.EventSymbol, eventAccess.IsUsableAsField, eventAccess.ResultKind, eventAccess.Type);
}
if (TransformCompoundAssignmentFieldOrEventAccessReceiver(eventAccess.EventSymbol, ref receiverOpt, stores, temps))
{
return MakeEventAccess(eventAccess.Syntax, receiverOpt, eventAccess.EventSymbol, eventAccess.ConstantValue, eventAccess.ResultKind, eventAccess.Type);
}
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(originalLHS.Kind);
}
......
......@@ -172,7 +172,7 @@ private BoundExpression RewriteWindowsRuntimeEventAssignmentOperator(SyntaxNode
return new BoundSequence(syntax, tempSymbols, sideEffects.ToImmutableAndFree(), marshalCall, marshalCall.Type);
}
private BoundExpression VisitWindowsRuntimeEventFieldAssignmentOperator(SyntaxNode syntax, BoundEventAccess left, BoundExpression right)
private BoundExpression VisitWindowsRuntimeEventFieldAssignmentOperator(SyntaxNode syntax, BoundEventAccess left, BoundExpression rewrittenRight)
{
Debug.Assert(left.IsUsableAsField);
......@@ -181,7 +181,6 @@ private BoundExpression VisitWindowsRuntimeEventFieldAssignmentOperator(SyntaxNo
Debug.Assert(eventSymbol.IsWindowsRuntimeEvent);
BoundExpression rewrittenReceiverOpt = left.ReceiverOpt == null ? null : VisitExpression(left.ReceiverOpt);
BoundExpression rewrittenRight = VisitExpression(right);
const bool isDynamic = false;
return RewriteWindowsRuntimeEventAssignmentOperator(
......
......@@ -6159,7 +6159,235 @@ public static void Main()
// (9,18): error CS8186: A foreach loop must declare its iteration variables.
// foreach ((arr[out int size], int b) in data) {}
Diagnostic(ErrorCode.ERR_MustDeclareForeachIteration, "(arr[out int size], int b)").WithLocation(9, 18)
);
}
[Fact]
[WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")]
public void Events_01()
{
string source = @"
class C
{
static event System.Action E;
static void Main()
{
(E, _) = (null, 1);
System.Console.WriteLine(E == null);
(E, _) = (Handler, 1);
E();
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
";
var comp = CompileAndVerify(source, expectedOutput:
@"True
Handler", additionalRefs: s_valueTupleRefs);
comp.VerifyDiagnostics();
}
[Fact]
[WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")]
public void Events_02()
{
string source = @"
struct S
{
event System.Action E;
class C
{
static void Main()
{
var s = new S();
(s.E, _) = (null, 1);
System.Console.WriteLine(s.E == null);
(s.E, _) = (Handler, 1);
s.E();
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
}
";
var comp = CompileAndVerify(source, expectedOutput:
@"True
Handler", additionalRefs: s_valueTupleRefs);
comp.VerifyDiagnostics();
}
[Fact]
[WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")]
public void Events_03()
{
string source1 = @"
public interface EventInterface
{
event System.Action E;
}
";
var comp1 = CreateCompilation(source1, WinRtRefs, TestOptions.ReleaseWinMD, TestOptions.Regular);
string source2 = @"
class C : EventInterface
{
public event System.Action E;
static void Main()
{
var c = new C();
c.Test();
}
void Test()
{
(E, _) = (null, 1);
System.Console.WriteLine(E == null);
(E, _) = (Handler, 1);
E();
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
";
var comp2 = CompileAndVerify(source2, expectedOutput:
@"True
Handler", additionalRefs: WinRtRefs.Concat(new[] { SystemRuntimeFacadeRef, ValueTupleRef, comp1.ToMetadataReference() }));
comp2.VerifyDiagnostics();
Assert.True(comp2.Compilation.GetMember<EventSymbol>("C.E").IsWindowsRuntimeEvent);
}
[Fact]
[WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")]
public void Events_04()
{
string source1 = @"
public interface EventInterface
{
event System.Action E;
}
";
var comp1 = CreateCompilation(source1, WinRtRefs, TestOptions.ReleaseWinMD, TestOptions.Regular);
string source2 = @"
struct S : EventInterface
{
public event System.Action E;
class C
{
S s = new S();
static void Main()
{
var c = new C();
(GetC(c).s.E, _) = (null, GetInt(1));
System.Console.WriteLine(c.s.E == null);
(GetC(c).s.E, _) = (Handler, GetInt(2));
c.s.E();
}
static int GetInt(int i)
{
System.Console.WriteLine(i);
return i;
}
static C GetC(C c)
{
System.Console.WriteLine(""GetC"");
return c;
}
static void Handler()
{
System.Console.WriteLine(""Handler"");
}
}
}
";
var comp2 = CompileAndVerify(source2, expectedOutput:
@"GetC
1
True
GetC
2
Handler", additionalRefs: WinRtRefs.Concat(new[] { SystemRuntimeFacadeRef, ValueTupleRef, comp1.ToMetadataReference() }));
comp2.VerifyDiagnostics();
Assert.True(comp2.Compilation.GetMember<EventSymbol>("S.E").IsWindowsRuntimeEvent);
}
[Fact]
[WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")]
public void Events_05()
{
string source = @"
class C
{
public static event System.Action E;
}
class Program
{
static void Main()
{
(C.E, _) = (null, 1);
}
}
";
var compilation = CreateCompilationWithMscorlib(source, references: s_valueTupleRefs);
compilation.VerifyDiagnostics(
// (11,12): error CS0070: The event 'C.E' can only appear on the left hand side of += or -= (except when used from within the type 'C')
// (C.E, _) = (null, 1);
Diagnostic(ErrorCode.ERR_BadEventUsage, "E").WithArguments("C.E", "C").WithLocation(11, 12)
);
}
[Fact]
[WorkItem(16962, "https://github.com/dotnet/roslyn/issues/16962")]
public void Events_06()
{
string source = @"
class C
{
static event System.Action E
{
add {}
remove {}
}
static void Main()
{
(E, _) = (null, 1);
}
}
";
var compilation = CreateCompilationWithMscorlib(source, references: s_valueTupleRefs);
compilation.VerifyDiagnostics(
// (12,10): error CS0079: The event 'C.E' can only appear on the left hand side of += or -=
// (E, _) = (null, 1);
Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(12, 10)
);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册