2016-02-23 33 views
9

Dopo un coupleshowstoppers migrazione ritardata al runtime .NET 4.6 Mi è stato finalmente comodo passare ai compilatori C# 6/VB14 fino a quando non ho trovato un problema critico con le funzioni iteratore in VB. NET buttando via variabili locali.La funzione Iterator VB.NET perde le variabili locali

Il seguente esempio di codice genera un'eccezione di riferimento null sulla riga commentata quando viene compilata in modalità di rilascio (ottimizzata) in Visual Studio 2015/msbuild.

Module Module1 

    Sub Main() 
     For Each o As Integer In GetAllStuff() 
      Console.WriteLine(o.ToString()) 
     Next 

     Console.ReadKey() 

    End Sub 

    Private Iterator Function GetAllStuff() As IEnumerable(Of Integer) 
     Dim map As Dictionary(Of String, String) = New Dictionary(Of String, String) 
     Dim tasks As New List(Of Integer) 
     tasks.Add(1) 

     For Each task As Integer In tasks 
      Yield task 
     Next 

     'The value of map becomes null here under the new VB14 compiler in Release on .NET 4.6' 
     For Each s As String In map.Values 
      Yield 100 
     Next 
    End Function 

End Module 

Quindi, questo è piuttosto terrificante.

In particolare l'equivalente C# di questo codice viene eseguito senza problemi. Ancora più importante, questo funziona (e ha funzionato) nelle versioni precedenti del compilatore VB. Confrontando MSIL tra la macchina a stati creata dai due diversi compilatori, sembra che il nuovo compilatore utilizzi .locals per la memorizzazione di variabili locali quasi esclusivamente mentre il vecchio compilatore utilizzava campi mutabili sulla macchina a stati per salvare i valori locali.

Mi manca qualcosa? Non sono stato in grado di trovare alcuna documentazione di un cambio di rottura con gli iteratori in VB (né posso immaginare che sarebbe il caso), ma non ho trovato nessun altro che abbia riscontrato questo problema.

Questo particolare esempio può essere risolto spostando la costruzione di map dopo il primo ciclo foreach, tuttavia la mia preoccupazione è che non ho alcun senso del vero sapore di questo problema. Non sono interessato a modificare il codice per "basta farlo funzionare". In quale altro posto nella nostra ampia base di codice potrei imbattersi in un problema di una stessa vena? Ho inviato il problema su Connect ma spesso mi sembra un buco nero.

UPDATE

Qualcuno ha appena segnalato lo stesso problema con la macchina statale asincrona sulla pagina Roslyn GitHub: https://github.com/dotnet/roslyn/issues/9001

Speriamo che questo comincia a diventare un po 'di attenzione.

+0

Ho eseguito il codice in VS2015, compilato su .NET Framework 4.5.2 e questo produce anche un 'NullReferenceException'. Quindi sembrerebbe non essere isolato in .NET 4.6 runtime. –

+0

Se compilo usando VS2013 contro 4.5.2 o 4.6, non ottengo il 'NullReferenceException'. Quindi, come dici tu, questo è un problema del compilatore, piuttosto che un problema di struttura. –

+0

@Threadcreator: il tuo codice funziona perfettamente per me. Utilizzo di Windows 10 Pro (x64), Visual Studio 2015 Express per Windows Desktop. Materiale di riferimento: 4.5 – SiriSch

risposta

1

Prima di tutto, grazie per aver attirato l'attenzione sul problema che ho sollevato con il team di Roslyn.

ho tirato l'ultima fonte Roslyn da https://github.com/dotnet/roslyn (ramo principale), e ha aggiunto un test di unità in più per il progetto BasicCompilerEmitTest che assomiglia a questo:

Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests 

Public Class KirillsTests 
    Inherits BasicTestBase 

    <Fact> 
    Public Sub IteratorVariableCaptureTest() 
    Dim source = 
<compilation name="Iterators"> 
    <file name="a.vb"> 
Imports System 
Imports System.Collections.Generic 

Module Module1 

    Sub Main() 
     For Each o As Integer In GetAllStuff() 
      Console.WriteLine(o.ToString()) 
     Next 

     Console.WriteLine("done") 
    End Sub 

    Private Iterator Function GetAllStuff() As IEnumerable(Of Integer) 
     Dim map As Dictionary(Of String, String) = New Dictionary(Of String, String) 
     Dim tasks As New List(Of Integer) 
     tasks.Add(1) 

     For Each task As Integer In tasks 
      Yield task 
     Next 

     'The value of map becomes null here under the new VB14 compiler in Release on .NET 4.6' 
     For Each s As String In map.Values 
      Yield 100 
     Next 
    End Function 

End Module 
    </file> 
</compilation> 

    Dim expectedOutput = <![CDATA[1 
done]]> 

    Dim compilation = CompilationUtils.CreateCompilationWithReferences(source, references:=LatestVbReferences, options:=TestOptions.DebugExe) 
    CompileAndVerify(compilation, expectedOutput:=expectedOutput) 
    CompileAndVerify(compilation.WithOptions(TestOptions.ReleaseExe), expectedOutput:=expectedOutput) 
    End Sub 

End Class 

Questo può apparire come un pasticcio contorta a causa di XElement e XCData, ma questo è il formato utilizzato da altri test dell'unità di Roslyn.

ho fatto solo una modifica al codice che avete inviato nella sua interrogazione - che sostituisce Console.ReadKey() con Console.WriteLine("done") così che ho potuto monitorare il completamento con successo (come CompileAndVerify ignora semplicemente eccezioni).

Il test di cui sopra passa. Non v'è alcun NullReferenceException su map.Values accesso, e l'uscita è:

 
1 
done 

... come previsto. Sembra quindi che il tuo bug sia stato corretto, anche se la correzione verrà fornita con Visual Studio 2015 Update 2, non posso dirlo.

Il async variable capture issue è stato fissato dal pull request #7693, ma DataFlowPass.SetSlotUnassigned è stato riscritto dal momento che (diviso in 2 metodi e modificato) quindi non posso confermare se il problema iteratore che hai scoperto è stato fissato dal quella specifica richiesta di pull, o da qualche altra modifica del codice.

+2

Questo è effettivamente affrontato con l'aggiornamento 2 CTP. Ho mandato un amico del telefono con il supporto Microsoft per ottenere maggiori dettagli nella correzione specifica e aggiornerò di nuovo con qualsiasi informazione mi venga fornita. Purtroppo, il 2015/4.6 si è sentito come un lungo CTP. La stabilità della piattaforma era un ottimo punto di vendita per .NET ... – roken

+1

@roken, utile sapere che la correzione verrà spedita a breve. Roslyn è ancora ai suoi esordi e hai ragione, ha un aspetto un po 'sperimentale. È un momento eccitante in molti modi, ma non sempre per una buona ragione, come dimostra questa domanda. –