diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 97c42b9e5665e..81007354f45d9 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -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 diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.cs index 4655cfcf35985..09183447d6176 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.cs @@ -1998,5 +1998,76 @@ void M(Action a1, Action a2, Action a3) VerifyFlowGraphAndDiagnosticsForTest(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) = /**/((int, Action))(1, M)/**/; + + 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(code, expectedOperationTree, DiagnosticDescription.None); + } + + [Fact, WorkItem(64774, "https://github.com/dotnet/roslyn/issues/64774")] + public void ExplicitCastOnTuple_02() + { + var code = """ + unsafe + { + (int i, delegate* invoke) = /**/((int, delegate*))(1, &M)/**/; + } + + static void M() {} + """; + + var comp = CreateCompilation(code, options: TestOptions.UnsafeReleaseExe); + comp.VerifyDiagnostics( + // (3,56): error CS0306: The type 'delegate*' may not be used as a type argument + // (int i, delegate* invoke) = /**/((int, delegate*))(1, &M)/**/; + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*").WithArguments("delegate*").WithLocation(3, 56) + ); + + var expectedOperationTree = """ +IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, delegate*), 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*)) (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*) (Syntax: '&M') + Reference: + IMethodReferenceOperation: void M() (Static) (OperationKind.MethodReference, Type: null) (Syntax: 'M') + Instance Receiver: + null +"""; + + VerifyOperationTreeForTest(comp, expectedOperationTree); + } } } diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.vb index 86103549a32e0..5aa0057a9b5e1 100644 --- a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.vb +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IDelegateCreationExpression.vb @@ -4401,6 +4401,44 @@ IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Action) VerifyOperationTreeAndDiagnosticsForTest(Of TryCastExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + + Public Sub ExplicitCastOnTuple() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of DirectCastExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + #End Region #Region "Anonymous Delegates"