dotnet / linker

389 stars 127 forks source link

Analyzer fails with System.InvalidOperationException #3046

Closed marek-safar closed 1 year ago

marek-safar commented 2 years ago

DynamicallyAccessedMembersAnalyzer fails with

'ILLink.RoslynAnalyzer.DynamicallyAccessedMembersAnalyzer' threw an exception of type 'System.InvalidOperationException' with message 'Operation is not valid due to the current state of the object.'.

detailed exception like

System.InvalidOperationException: Operation is not valid due to the current state of the object. (TaskId:188)
 at ILLink.Shared.DataFlow.DictionaryLattice`3.Meet(DefaultValueDictionary`2 left, DefaultValueDictionary`2 right) (TaskId:188)
 at ILLink.RoslynAnalyzer.DataFlow.LocalStateLattice`2.Meet(LocalState`1 left, LocalState`1 right) (TaskId:188)
 at ILLink.Shared.DataFlow.ForwardDataFlowAnalysis`7.Fixpoint(TControlFlowGraph cfg, TLattice lattice, TTransfer transfer) (TaskId:188)
 at ILLink.RoslynAnalyzer.DataFlow.LocalDataFlowAnalysis`3.AnalyzeMethod(MethodBodyValue method, InterproceduralState`2& interproceduralState) (TaskId:188)
 at ILLink.RoslynAnalyzer.DataFlow.LocalDataFlowAnalysis`3.InterproceduralAnalyze() (TaskId:188)
 at ILLink.RoslynAnalyzer.DynamicallyAccessedMembersAnalyzer.<>c.<Initialize>b__7_1(OperationBlockAnalysisContext context) (TaskId:188)
 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__67`2.<ExecuteBlockActions>b__67_1(ValueTuple`2 data) (TaskId:188)
 at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)

when encountering ref fields access for example this one https://github.com/dotnet/runtime/blob/4cf2e3bb18162d750e7a128bb805c49c9fd52612/src/libraries/System.Private.CoreLib/src/System/Threading/Overlapped.cs#L46

vitek-karas commented 2 years ago

It fails here: https://github.com/dotnet/linker/blob/9503e0b2e184bbd64a7e1a2383584c370d70031a/src/ILLink.RoslynAnalyzer/DataFlow/CapturedReferenceValue.cs#L55

The reason is code like:

(b ? ref _field1 : ref _field2) = 2;

The if/then/else ends with a merge of two state where one captures _field1 and the other _field2, so we need to merge two non-empty captured references. That has not been implemented yet.

I'm afraid that the only correct solution is to support something like MultiValue<CapturedReferenceValue> or for CapturedReferenceValue to store potentially multiple references. That also means that all consumption sides have to iterave over all possible values.