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

Fix for "Function pointer invocation has "None" IOperation" #57191

Merged
merged 48 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d7b1cef
inital draft for https://github.com/dotnet/roslyn/issues/48082
bernd5 Oct 17, 2021
fe98a28
indention
bernd5 Oct 17, 2021
758ba9b
introduction of an FunctionPointerInvocationOperation (without updati…
bernd5 Oct 17, 2021
165a921
test adjustments for IFunctionPointerInvocationOperation
bernd5 Oct 17, 2021
97232e2
name adjusted
bernd5 Oct 17, 2021
b215196
fixed API compatibility issue caused by OperationKind-order
bernd5 Oct 17, 2021
89857b5
FunctionPointerInvocation
bernd5 Oct 17, 2021
5a22df6
IFunctionPointerInvocationOperation.Pointer -> IFunctionPointerInvoca…
bernd5 Oct 19, 2021
a12bee4
IFunctionPointerInvocationOperation.Pointer -> IFunctionPointerInvoca…
bernd5 Oct 19, 2021
4c6c870
IFunctionPointerInvocationOperation.Pointer -> IFunctionPointerInvoca…
bernd5 Oct 19, 2021
39118dd
base interface try
bernd5 Oct 29, 2021
b7b1d88
Revert "base interface try"
bernd5 Oct 29, 2021
878ce23
IFunctionPointerInvocationOperation.InvokedPointer -> IFunctionPointe…
bernd5 Oct 29, 2021
d0a22f3
static Microsoft.CodeAnalysis.Operations.OperationExtensions.GetFunct…
bernd5 Oct 29, 2021
ef219a2
Merge remote-tracking branch 'dotnet-roslyn/main' into fix_FunctionPo…
bernd5 Oct 29, 2021
352ffeb
Pointer -> Target
bernd5 Oct 29, 2021
4fad1bb
Pointer -> Target
bernd5 Oct 29, 2021
585c945
API Change
bernd5 Oct 29, 2021
84d369f
used GetFunctionPointerSignature in TestOperationVisitor
bernd5 Oct 29, 2021
24d8477
test added
bernd5 Oct 29, 2021
52dd8f0
test added
bernd5 Oct 29, 2021
df5aa01
created function VisitFunctionPointerWithArguments
bernd5 Oct 29, 2021
7aea445
created function VisitFunctionPointerWithArguments
bernd5 Oct 29, 2021
0273767
inline VisitFunctionPointerWithArguments
bernd5 Oct 30, 2021
150bc5e
Equal -> Same
bernd5 Oct 30, 2021
8785096
Merge branch 'main' of https://github.com/dotnet/roslyn into fix_Func…
bernd5 Oct 30, 2021
c56677f
return null if type is null
bernd5 Oct 30, 2021
2e45411
nullable annotation removed
bernd5 Oct 30, 2021
b1c6724
IOperation? -> IOperation
bernd5 Nov 1, 2021
c49e938
Add `ITypeSymbol` for NoneOperation if available
bernd5 Nov 1, 2021
50bf09f
GetFunctionPointerSignature should return not null
bernd5 Nov 1, 2021
1d124b3
TestOperationVisitor check added
bernd5 Nov 1, 2021
dc75c35
use switch expression
bernd5 Nov 1, 2021
f0bca7e
API
bernd5 Nov 1, 2021
60c4ce5
Merge remote-tracking branch 'origin/main' into fix_FunctionPointerOp…
bernd5 Nov 1, 2021
1839391
workaround: ignore type for tests
bernd5 Nov 1, 2021
17b09e0
test added
bernd5 Nov 1, 2021
98e5c37
condition for workaround extended
bernd5 Nov 2, 2021
fdafc02
introduced WorkaroundNoneOperation
bernd5 Nov 2, 2021
d076cce
adjusted CFG for workaround
bernd5 Nov 2, 2021
8eb4e48
Revert "workaround extended"
bernd5 Nov 2, 2021
f9eebf0
OperationTreeVerifier check adjusted (OperationKind.None is not only …
bernd5 Nov 2, 2021
4e73175
remove type in test
bernd5 Nov 2, 2021
20edb23
issue linked
bernd5 Nov 2, 2021
0d937d4
comment adjusted
bernd5 Nov 3, 2021
e14c410
Merge remote-tracking branch 'origin/main' into fix_FunctionPointerOp…
bernd5 Nov 3, 2021
2c03952
Merge remote-tracking branch 'origin/main' into fix_FunctionPointerOp…
bernd5 Nov 3, 2021
9a0ec00
suppression removed
bernd5 Nov 4, 2021
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 @@ -314,10 +314,13 @@ public CSharpOperationFactory(SemanticModel semanticModel)
break;
}
}

ImmutableArray<IOperation> children = GetIOperationChildren(boundNode);
return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: null, constantValue, isImplicit: isImplicit);

ITypeSymbol? type = boundNode switch
{
BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(),
_ => null
};
return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit);
default:
// If you're hitting this because the IOperation test hook has failed, see
// <roslyn-root>/docs/Compilers/IOperation Test Hook.md for instructions on how to fix.
Expand Down Expand Up @@ -453,16 +456,16 @@ private IOperation CreateBoundFunctionPointerInvocationOperation(BoundFunctionPo
ITypeSymbol? type = boundFunctionPointerInvocation.GetPublicTypeSymbol();
SyntaxNode syntax = boundFunctionPointerInvocation.Syntax;
bool isImplicit = boundFunctionPointerInvocation.WasCompilerGenerated;
ImmutableArray<IOperation> children;

if (boundFunctionPointerInvocation.ResultKind != LookupResultKind.Viable)
{
children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundFunctionPointerInvocation).InvalidNodeChildren);
ImmutableArray<IOperation> children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundFunctionPointerInvocation).InvalidNodeChildren);
return new InvalidOperation(children, _semanticModel, syntax, type, constantValue: null, isImplicit);
}

children = GetIOperationChildren(boundFunctionPointerInvocation);
return new NoneOperation(children, _semanticModel, syntax, type, constantValue: null, isImplicit);
var pointer = Create(boundFunctionPointerInvocation.InvokedExpression);
var arguments = DeriveArguments(boundFunctionPointerInvocation);
return new FunctionPointerInvocationOperation(pointer, arguments, _semanticModel, syntax, type, isImplicit);
}

private IOperation CreateBoundUnconvertedAddressOfOperatorOperation(BoundUnconvertedAddressOfOperator boundUnconvertedAddressOf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,17 @@ internal ImmutableArray<IArgumentOperation> DeriveArguments(BoundNode containing
boundCollectionElementInitializer.Syntax,
boundCollectionElementInitializer.InvokedAsExtensionMethod);
}

case BoundKind.FunctionPointerInvocation:
{
var boundFunctionPointerInvocation = (BoundFunctionPointerInvocation)containingExpression;
return DeriveArguments(boundFunctionPointerInvocation.FunctionPointer.Signature,
boundFunctionPointerInvocation.Arguments,
default,
BitVector.Empty,
false,
boundFunctionPointerInvocation.Syntax,
false);
}
default:
throw ExceptionUtilities.UnexpectedValue(containingExpression.Kind);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;

Expand Down Expand Up @@ -187,6 +188,67 @@ static void M2()
VerifyOperationTreeAndDiagnosticsForTest<PrefixUnaryExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[Fact]
public void FunctionPointerInvocationSignatureTest()
{
var comp = CreateFunctionPointerCompilation(@"
unsafe class C
{
public string Prop { get; }
void M(delegate*<string, void> ptr)
{
/*<bind>*/ptr(Prop)/*</bind>*/;
}
}");
var (actualOperation, syntaxNode) = GetOperationAndSyntaxForTest<InvocationExpressionSyntax>(comp);

var fktPointerOp = (IFunctionPointerInvocationOperation)actualOperation;
var signature = fktPointerOp.GetFunctionPointerSignature();

Assert.NotNull(syntaxNode);
Assert.Equal(1, signature.Parameters.Length);
Assert.Equal(SpecialType.System_String, signature.Parameters[0].Type.SpecialType);
Assert.Equal(SpecialType.System_Void, signature.ReturnType.SpecialType);
}

[Fact]
public void FunctionPointerUnsafe()
{
var comp = CreateFunctionPointerCompilation(@"
using System;
static unsafe class C
{
static int Getter(int i) => i;
static void Print(delegate*<int, int>* p)
{
for (int i = 0; i < 3; i++)
Console.Write(/*<bind>*/p[i](i)/*</bind>*/);
}

static void Main()
{
delegate*<int, int>* p = stackalloc delegate*<int, int>[] { &Getter, &Getter, &Getter };
Print(p);
}
}
");
var expectedOperationTree = @"
IFunctionPointerInvocationOperation (OperationKind.FunctionPointerInvocation, Type: System.Int32) (Syntax: 'p[i](i)')
Target:
IOperation: (OperationKind.None, Type: null) (Syntax: 'p[i]')
Children(2):
IParameterReferenceOperation: p (OperationKind.ParameterReference, Type: delegate*<System.Int32, System.Int32>*) (Syntax: 'p')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: ) (OperationKind.Argument, Type: null) (Syntax: 'i')
ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
";

VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics: new DiagnosticDescription[0]);
}

[Fact]
public void FunctionPointerInvocation()
{
Expand All @@ -201,12 +263,16 @@ void M(delegate*<string, void> ptr)
}");

var expectedOperationTree = @"
IOperation: (OperationKind.None, Type: System.Void) (Syntax: 'ptr(Prop)')
Children(2):
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Void>) (Syntax: 'ptr')
IPropertyReferenceOperation: System.String C.Prop { get; } (OperationKind.PropertyReference, Type: System.String) (Syntax: 'Prop')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Prop')
IFunctionPointerInvocationOperation (OperationKind.FunctionPointerInvocation, Type: System.Void) (Syntax: 'ptr(Prop)')
Target:
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Void>) (Syntax: 'ptr')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: ) (OperationKind.Argument, Type: null) (Syntax: 'Prop')
IPropertyReferenceOperation: System.String C.Prop { get; } (OperationKind.PropertyReference, Type: System.String) (Syntax: 'Prop')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'Prop')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
";

VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics: new DiagnosticDescription[0]);
Expand Down Expand Up @@ -326,34 +392,42 @@ void M(delegate*<string, int> ptr)
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'string s = ptr(Prop)')
Declarators:
IVariableDeclaratorOperation (Symbol: System.String s) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 's = ptr(Prop)')
Initializer:
Initializer:
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= ptr(Prop)')
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsInvalid, IsImplicit) (Syntax: 'ptr(Prop)')
Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IOperation: (OperationKind.None, Type: System.Int32, IsInvalid) (Syntax: 'ptr(Prop)')
Children(2):
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Int32>, IsInvalid) (Syntax: 'ptr')
IPropertyReferenceOperation: System.String C.Prop { get; } (OperationKind.PropertyReference, Type: System.String, IsInvalid) (Syntax: 'Prop')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'Prop')
Initializer:
Operand:
IFunctionPointerInvocationOperation (OperationKind.FunctionPointerInvocation, Type: System.Int32, IsInvalid) (Syntax: 'ptr(Prop)')
Target:
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Int32>, IsInvalid) (Syntax: 'ptr')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: ) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: 'Prop')
IPropertyReferenceOperation: System.String C.Prop { get; } (OperationKind.PropertyReference, Type: System.String, IsInvalid) (Syntax: 'Prop')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'Prop')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Initializer:
null
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 's = ptr(Prop);')
Expression:
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String, IsInvalid) (Syntax: 's = ptr(Prop)')
Left:
Left:
ILocalReferenceOperation: s (OperationKind.LocalReference, Type: System.String) (Syntax: 's')
Right:
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.String, IsInvalid, IsImplicit) (Syntax: 'ptr(Prop)')
Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IOperation: (OperationKind.None, Type: System.Int32, IsInvalid) (Syntax: 'ptr(Prop)')
Children(2):
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Int32>, IsInvalid) (Syntax: 'ptr')
IPropertyReferenceOperation: System.String C.Prop { get; } (OperationKind.PropertyReference, Type: System.String, IsInvalid) (Syntax: 'Prop')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'Prop')
Operand:
IFunctionPointerInvocationOperation (OperationKind.FunctionPointerInvocation, Type: System.Int32, IsInvalid) (Syntax: 'ptr(Prop)')
Target:
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Int32>, IsInvalid) (Syntax: 'ptr')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: ) (OperationKind.Argument, Type: null, IsInvalid) (Syntax: 'Prop')
IPropertyReferenceOperation: System.String C.Prop { get; } (OperationKind.PropertyReference, Type: System.String, IsInvalid) (Syntax: 'Prop')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'Prop')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
";

var expectedDiagnostics = new DiagnosticDescription[] {
Expand Down Expand Up @@ -463,44 +537,55 @@ static void Test(delegate*<string, void> ptr, bool b, string s1, string s2)
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}

.locals {R1}
{
CaptureIds: [0] [1]
Block[B1] - Block
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ptr')
Value:
Value:
IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.String, System.Void>) (Syntax: 'ptr')

Jump if False (Regular) to Block[B3]
IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b')

Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 's1')
Value:
Value:
IParameterReferenceOperation: s1 (OperationKind.ParameterReference, Type: System.String) (Syntax: 's1')

Next (Regular) Block[B4]
Block[B3] - Block
Predecessors: [B1]
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 's2')
Value:
Value:
IParameterReferenceOperation: s2 (OperationKind.ParameterReference, Type: System.String) (Syntax: 's2')

Next (Regular) Block[B4]
Block[B4] - Block
Predecessors: [B2] [B3]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'ptr(b ? s1 : s2);')
Expression:
IOperation: (OperationKind.None, Type: System.Void) (Syntax: 'ptr(b ? s1 : s2)')
Children(2):
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: delegate*<System.String, System.Void>, IsImplicit) (Syntax: 'ptr')
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.String, IsImplicit) (Syntax: 'b ? s1 : s2')
Expression:
IFunctionPointerInvocationOperation (OperationKind.FunctionPointerInvocation, Type: System.Void) (Syntax: 'ptr(b ? s1 : s2)')
Target:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: delegate*<System.String, System.Void>, IsImplicit) (Syntax: 'ptr')
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: ) (OperationKind.Argument, Type: null) (Syntax: 'b ? s1 : s2')
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.String, IsImplicit) (Syntax: 'b ? s1 : s2')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)

Next (Regular) Block[B5]
Leaving: {R1}
}

Block[B5] - Exit
Predecessors: [B4]
Statements (0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2308,7 +2308,7 @@ public CustomHandler(int literalLength, int formattedCount, C c, bool b, out boo
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: c) (OperationKind.Argument, Type: null) (Syntax: '$""literal{c,1:format}""')
IOperation: (OperationKind.None, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{c,1:format}""')
IOperation: (OperationKind.None, Type: null, IsImplicit) (Syntax: '$""literal{c,1:format}""')
Children(2):
IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, C c, System.Boolean b, out System.Boolean success)) (OperationKind.ObjectCreation, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{c,1:format}""')
Arguments(5):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,5 +267,7 @@ public enum OperationKind
InterpolatedStringAppendInvalid = 0x76,
/// <summary>Indicates an <see cref="IInterpolatedStringHandlerArgumentPlaceholderOperation"/>.</summary>
InterpolatedStringHandlerArgumentPlaceholder = 0x77,
/// <summary>Indicates an <see cref="IFunctionPointerInvocationOperation"/>.</summary>
FunctionPointerInvocation = 0x78,
}
}
Loading