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

Unexpected control flow graph verification failure for a erroneous nested collection initializer #72931

Open
AlekseyTs opened this issue Apr 8, 2024 · 0 comments

Comments

@AlekseyTs
Copy link
Contributor

        [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)]
        [Fact]
        public void ObjectCreationFlow_75_CollectionInitializerError()
        {
            string source = @"
public class C
{
    public static void Main()
    /*<bind>*/{
        int d = 1;
        var c = new C() { [d] = {2} };
    }/*</bind>*/

    C2 _test1 = new C2();    
    C2 this[int x]
    {
        get => _test1;
    }
}

class C2
{
}
";
            var expectedDiagnostics = new[] {
                // (7,33): error CS1922: Cannot initialize type 'C2' with a collection initializer because it does not implement 'System.Collections.IEnumerable'
                //         var c = new C() { [d] = {2} };
                Diagnostic(ErrorCode.ERR_CollectionInitRequiresIEnumerable, "{2}").WithArguments("C2").WithLocation(7, 33)
                };

            string expectedFlowGraph = @"
";
            VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedFlowGraph, expectedDiagnostics);
        }

Observed:

    Capture [1] is not used in region [R3] before leaving it after block [B3]
    Block[B0] - Entry
        Statements (0)
        Next (Regular) Block[B1]
            Entering: {R1}
    
    .locals {R1}
    {
        Locals: [System.Int32 d] [C c]
        Block[B1] - Block
            Predecessors: [B0]
            Statements (1)
                ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsImplicit) (Syntax: 'd = 1')
                  Left: 
                    ILocalReferenceOperation: d (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'd = 1')
                  Right: 
                    ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
    
            Next (Regular) Block[B2]
                Entering: {R2}
    
        .locals {R2}
        {
            CaptureIds: [0]
            Block[B2] - Block
                Predecessors: [B1]
                Statements (1)
                    IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'new C() { [d] = {2} }')
                      Value: 
                        IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C() { [d] = {2} }')
                          Arguments(0)
                          Initializer: 
                            null
    
                Next (Regular) Block[B3]
                    Entering: {R3}
    
            .locals {R3}
            {
                CaptureIds: [1]
                Block[B3] - Block
                    Predecessors: [B2]
                    Statements (2)
                        IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'd')
                          Value: 
                            ILocalReferenceOperation: d (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'd')
    
                        IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid, IsImplicit) (Syntax: '2')
                          Children(1):
                              ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2')
    
                    Next (Regular) Block[B4]
                        Leaving: {R3}
            }
    
            Block[B4] - Block
                Predecessors: [B3]
                Statements (1)
                    ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C, IsInvalid, IsImplicit) (Syntax: 'c = new C() ... [d] = {2} }')
                      Left: 
                        ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'c = new C() ... [d] = {2} }')
                      Right: 
                        IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'new C() { [d] = {2} }')
    
                Next (Regular) Block[B5]
                    Leaving: {R2} {R1}
        }
    }
    
    Block[B5] - Exit
        Predecessors: [B4]
        Statements (0)
    
    Expected: True
    Actual:   False

Either we should relax the validation to specifically target the scenario, or we can slightly adjust the graph creation so that it included the capture as a child of an IInvalidOperation representing erroneous element add. The second is probably going to be easier to implement.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Apr 8, 2024
AlekseyTs added a commit that referenced this issue Apr 11, 2024
…c` arguments (#72964)

Fixes #72750.

For C# 12 language version: behavior of the compiler in regards to deciding between whether binding should be static or dynamic is the same as behavior of C# 12 compiler. As a result, for affected scenarios, what used to have `dynamic` type in C# 12 compiler will have `dynamic` type when C# 12 language version is targeted.

For newer language versions: invocations statically bound in presence of dynamic arguments should have dynamic result if their dynamic binding succeeded in C# 12. Corresponding spec update - dotnet/csharplang#8027 at commit 8.

Related to #33011, #72906, #72912, #72913, #72914, #72916, #72931.
AlekseyTs added a commit to AlekseyTs/roslyn that referenced this issue Apr 16, 2024
…c` arguments (dotnet#72964)

Fixes dotnet#72750.

For C# 12 language version: behavior of the compiler in regards to deciding between whether binding should be static or dynamic is the same as behavior of C# 12 compiler. As a result, for affected scenarios, what used to have `dynamic` type in C# 12 compiler will have `dynamic` type when C# 12 language version is targeted.

For newer language versions: invocations statically bound in presence of dynamic arguments should have dynamic result if their dynamic binding succeeded in C# 12. Corresponding spec update - dotnet/csharplang#8027 at commit 8.

Related to dotnet#33011, dotnet#72906, dotnet#72912, dotnet#72913, dotnet#72914, dotnet#72916, dotnet#72931.
AlekseyTs added a commit that referenced this issue Apr 17, 2024
Restore dynamic as result type of some operations involving dynamic arguments (#72964)

Fixes #72750.

For C# 12 language version: behavior of the compiler in regards to deciding between whether binding should be static or dynamic is the same as behavior of C# 12 compiler. As a result, for affected scenarios, what used to have dynamic type in C# 12 compiler will have dynamic type when C# 12 language version is targeted.

For newer language versions: invocations statically bound in presence of dynamic arguments should have dynamic result if their dynamic binding succeeded in C# 12. Corresponding spec update - dotnet/csharplang#8027 at commit 8.

Related to #33011, #72906, #72912, #72913, #72914, #72916, #72931.
@jaredpar jaredpar added Bug and removed untriaged Issues and PRs which have not yet been triaged by a lead labels Apr 22, 2024
@jaredpar jaredpar added this to the Backlog milestone Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants