Skip to content

Commit

Permalink
Account for delegate creation inside an explicitly-converted tuple ex…
Browse files Browse the repository at this point in the history
…pression (#64882)

Fixes #64774.
  • Loading branch information
333fred authored Oct 26, 2022
1 parent 4786ca0 commit b580572
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,14 @@ private IOperation CreateBoundConversionOperation(BoundConversion boundConversio
// overload resolution succeeded. The resulting method could be invalid for other reasons, but we don't
// hide the resolved method.
IOperation target = CreateDelegateTargetOperation(boundConversion);

// If this was an explicit tuple expression conversion, such as ((Action, int))(M, 1), we will be "explicit", because the
// original conversion was explicit in code, but the syntax node for this delegate creation and the nested method group will
// be the same. We therefore need to mark this node as implicit to ensure we don't have two explicit nodes for the same syntax.
Debug.Assert(isImplicit || target.Syntax != syntax || target.IsImplicit || boundConversion.ConversionGroupOpt != null);

isImplicit = isImplicit || (target.Syntax == syntax && !target.IsImplicit);

return new DelegateCreationOperation(target, _semanticModel, syntax, type, isImplicit);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1998,5 +1998,76 @@ void M(Action a1, Action a2, Action a3)

VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedFlowGraph, expectedDiagnostics);
}

[Fact, WorkItem(64774, "https://github.com/dotnet/roslyn/issues/64774")]
public void ExplicitCastOnTuple_01()
{
var code = """
using System;
(int i, Action invoke) = /*<bind>*/((int, Action))(1, M)/*</bind>*/;
void M() {}
""";

var expectedOperationTree = """
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Action)) (Syntax: '((int, Action))(1, M)')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Action M)) (Syntax: '(1, M)')
NaturalType: null
Elements(2):
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '1')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action, IsImplicit) (Syntax: 'M')
Target:
IMethodReferenceOperation: void M() (OperationKind.MethodReference, Type: null) (Syntax: 'M')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Program, IsImplicit) (Syntax: 'M')
""";
VerifyOperationTreeAndDiagnosticsForTest<CastExpressionSyntax>(code, expectedOperationTree, DiagnosticDescription.None);
}

[Fact, WorkItem(64774, "https://github.com/dotnet/roslyn/issues/64774")]
public void ExplicitCastOnTuple_02()
{
var code = """
unsafe
{
(int i, delegate*<void> invoke) = /*<bind>*/((int, delegate*<void>))(1, &M)/*</bind>*/;
}
static void M() {}
""";

var comp = CreateCompilation(code, options: TestOptions.UnsafeReleaseExe);
comp.VerifyDiagnostics(
// (3,56): error CS0306: The type 'delegate*<void>' may not be used as a type argument
// (int i, delegate*<void> invoke) = /*<bind>*/((int, delegate*<void>))(1, &M)/*</bind>*/;
Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*<void>").WithArguments("delegate*<void>").WithLocation(3, 56)
);

var expectedOperationTree = """
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, delegate*<System.Void>), IsInvalid) (Syntax: '((int, dele ... d>))(1, &M)')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ITupleOperation (OperationKind.Tuple, Type: (System.Int32, delegate*<System.Void>)) (Syntax: '(1, &M)')
NaturalType: null
Elements(2):
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '1')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
IAddressOfOperation (OperationKind.AddressOf, Type: delegate*<System.Void>) (Syntax: '&M')
Reference:
IMethodReferenceOperation: void M() (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'M')
Instance Receiver:
null
""";

VerifyOperationTreeForTest<CastExpressionSyntax>(comp, expectedOperationTree);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4401,6 +4401,44 @@ IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action)
VerifyOperationTreeAndDiagnosticsForTest(Of TryCastExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub

<CompilerTrait(CompilerFeature.IOperation)>
<Fact()>
Public Sub ExplicitCastOnTuple()
Dim source = <![CDATA[
Imports System

Public Class C
Public Sub M1()
Dim x = DirectCast((AddressOf M2, 1), (Action, Integer))'BIND:"DirectCast((AddressOf M2, 1), (Action, Integer))"
End Sub

Public Sub M2()
End Sub
End Class]]>.Value

Dim expectedOperationTree = <![CDATA[
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Action, System.Int32)) (Syntax: 'DirectCast( ... , Integer))')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ITupleOperation (OperationKind.Tuple, Type: (System.Action, System.Int32)) (Syntax: '(AddressOf M2, 1)')
NaturalType: null
Elements(2):
IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action, IsImplicit) (Syntax: 'AddressOf M2')
Target:
IMethodReferenceOperation: Sub C.M2() (OperationKind.MethodReference, Type: null) (Syntax: 'AddressOf M2')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'M2')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '1')
Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
]]>.Value

Dim expectedDiagnostics = String.Empty

VerifyOperationTreeAndDiagnosticsForTest(Of DirectCastExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics)
End Sub

#End Region

#Region "Anonymous Delegates"
Expand Down

0 comments on commit b580572

Please sign in to comment.