Skip to content

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

@AlekseyTs

Description

@AlekseyTs
        [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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions