dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.05k stars 4.04k forks source link

Different types for [non]implicit array creation in the ForEach VB #25224

Open GeorgeAlexandria opened 6 years ago

GeorgeAlexandria commented 6 years ago

Hi,

I'm used Microsoft.CodeAnalysis.VisualBasic.1.3.2 and they dependents.

I'm try to analyze the code below:

...
    Sub Test()

        For Each list1 In {"111", "222"}
        Next

        For Each list2 In New String() {"333", "444"}
        Next

    End Sub

Option Infer is disabled and option strict is enabled in the corresponding compilation.

And I was surprised when I retrieved the different TypeInfo from SemanticModel for a CollectionInitializerSyntax nodes in the both of case ({"111", "222"}, {"333", "444"}).

In the second case I retrieved info that was containing Array of string type as I expected, but in the first case I get non generic IEnumerable type. I tried to retrieve conversion for the first case, but it also didn't return to me Array of string type.

In the both cases these types are known at the compile time, they are Array of string and have the identical IL code:

IL code ``` .method public static void Test () cil managed { .maxstack 4 .locals init ( [0] string[], [1] int32, [2] string list1, [3] bool, [4] string[], [5] int32, [6] string list2, [7] bool ) IL_0000: nop IL_0001: ldc.i4.2 IL_0002: newarr [mscorlib]System.String IL_0007: dup IL_0008: ldc.i4.0 IL_0009: ldstr "111" IL_000E: stelem.ref IL_000F: dup IL_0010: ldc.i4.1 IL_0011: ldstr "222" IL_0016: stelem.ref IL_0017: stloc.0 IL_0018: ldc.i4.0 IL_0019: stloc.1 IL_001A: br.s IL_0025 // loop start (head: IL_0025) IL_001C: ldloc.0 IL_001D: ldloc.1 IL_001E: ldelem.ref IL_001F: stloc.2 IL_0020: nop IL_0021: ldloc.1 IL_0022: ldc.i4.1 IL_0023: add.ovf IL_0024: stloc.1 IL_0025: ldloc.1 IL_0026: ldloc.0 IL_0027: ldlen IL_0028: conv.i4 IL_0029: clt IL_002B: stloc.3 IL_002C: ldloc.3 IL_002D: brtrue.s IL_001C // end loop IL_002F: ldc.i4.2 IL_0030: newarr [mscorlib]System.String IL_0035: dup IL_0036: ldc.i4.0 IL_0037: ldstr "333" IL_003C: stelem.ref IL_003D: dup IL_003E: ldc.i4.1 IL_003F: ldstr "444" IL_0044: stelem.ref IL_0045: stloc.s V_4 IL_0047: ldc.i4.0 IL_0048: stloc.s V_5 IL_004A: br.s IL_005A // loop start (head: IL_005A) IL_004C: ldloc.s V_4 IL_004E: ldloc.s V_5 IL_0050: ldelem.ref IL_0051: stloc.s list2 IL_0053: nop IL_0054: ldloc.s V_5 IL_0056: ldc.i4.1 IL_0057: add.ovf IL_0058: stloc.s V_5 IL_005A: ldloc.s V_5 IL_005C: ldloc.s V_4 IL_005E: ldlen IL_005F: conv.i4 IL_0060: clt IL_0062: stloc.s V_7 IL_0064: ldloc.s V_7 IL_0066: brtrue.s IL_004C // end loop IL_0068: ret } // end of method Module1::Test ```

So, does it incorrect behavior|bug for the implicit array creation? Do I understand something wrong? By the way, I can reproduce it only when CollectionInitializeSyntax is a ForEachStatementSyntax.Expression, for example, in the AssignmentStatement both of case work identically as expected.

If it's a bug can you give some suggestions how I can workaround it and retrieve the correct type? (Of course, array can have more dimensions than one)

Any suggestion would be grateful.

AdamSpeight2008 commented 6 years ago

@GeorgeAlexandria

What do you get if you enclosing the array in parenthesis? eg

    Sub Test()

        For Each list1 In {"111", "222"}
        Next

        For Each list2 In (New String() {"333", "444"})
        Next

    End Sub

and also could you try the following

    Sub Test()

        For Each list1 In {"111", "222"}
        Next

        For Each list2 In New String() From {"333", "444"}
        Next

    End Sub
GeorgeAlexandria commented 6 years ago

@AdamSpeight2008, Apologies, I replaced cases in the description by mistake. Now I fixed description, you can check it again. So I have problem with the first case.

jcouv commented 6 years ago

@GeorgeAlexandria What if you use ForEachStatementInfo to inspect the type of collection being enumerated?

GeorgeAlexandria commented 6 years ago

@jcouv, I retrieved correct only the collection element type using ForEachStatementInfo, GetEnumerationMehod returned non generic System.Collection.IEnumerable and narrowing conversion from IEnumerator.Current (object) to collection element type.

Also ForEachStatementInfo returns not correct data, for example, in the code bellow:

...
        Class A
        End Class

        Class B
        End Class

    Sub Test()

       For Each list As B In {New A(), New B()}
       Next

    End Sub

ForEachStatementInfo.ElementType was Object, but expected B in this example. Consequently the not correct conversion will be applied. Also GetSymbolInfo and GeTypeInfo for ForOrForEachStatementSyntax.ControlVariable didn't return a correct type in the example above.