From 58ba8565bffd1a6c00f9dfce010c254604e316dc Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 15 Jan 2025 13:40:48 -0800 Subject: [PATCH 1/4] Add a test that fails --- .../SourceGeneration/GeneratorDriverTests.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests.cs b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests.cs index 3389f2b5169e8..d0d009e255a24 100644 --- a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests.cs @@ -1525,6 +1525,54 @@ [Attr] class D { } Assert.Equal(e, runResults.Results.Single().Exception); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76765")] + public void Incremental_Generators_Exception_In_DefaultComparer() + { + var source = """ + class C { } + """; + var parseOptions = TestOptions.RegularPreview; + Compilation compilation = CreateCompilation(source, options: TestOptions.DebugDllThrowing, parseOptions: parseOptions); + compilation.VerifyDiagnostics(); + + var syntaxTree = compilation.SyntaxTrees.Single(); + + var e = new InvalidOperationException("abc"); + var generator = new PipelineCallbackGenerator((ctx) => + { + var name = ctx.CompilationProvider.Select((c, _) => new ThrowWhenEqualsItem(e)); + ctx.RegisterSourceOutput(name, (spc, n) => spc.AddSource("item.cs", "// generated")); + }); + + GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions); + driver = driver.RunGenerators(compilation); + var runResults = driver.GetRunResult(); + + Assert.Empty(runResults.Diagnostics); + Assert.Equal("// generated", runResults.Results.Single().GeneratedSources.Single().SourceText.ToString()); + + compilation = compilation.ReplaceSyntaxTree(syntaxTree, CSharpSyntaxTree.ParseText(""" + class D { } + """, parseOptions)); + compilation.VerifyDiagnostics(); + + driver = driver.RunGenerators(compilation); + runResults = driver.GetRunResult(); + + VerifyGeneratorExceptionDiagnostic(runResults.Diagnostics.Single(), nameof(PipelineCallbackGenerator), "abc"); + Assert.Empty(runResults.GeneratedTrees); + Assert.Equal(e, runResults.Results.Single().Exception); + } + + class ThrowWhenEqualsItem(Exception toThrow) + { + readonly Exception _toThrow = toThrow; + + public override bool Equals(object? obj) => throw _toThrow; + + public override int GetHashCode() => throw new NotImplementedException(); + } + [Fact] public void Incremental_Generators_Exception_During_Execution_Doesnt_Produce_AnySource() { From b766433783e094cd20713e370e3870e4b9da6d6d Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 15 Jan 2025 14:55:51 -0800 Subject: [PATCH 2/4] Clean up strategies that always have a comparer --- .../Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs | 2 +- .../Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs index 540902c29ac9e..682ca1764f9cd 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs @@ -30,7 +30,7 @@ internal PredicateSyntaxStrategy( _filterFunc = filterFunc; } - public ISyntaxInputBuilder GetBuilder(StateTableStore table, object key, bool trackIncrementalSteps, string? name, IEqualityComparer? comparer) => new Builder(this, key, table, trackIncrementalSteps, name, comparer ?? EqualityComparer.Default); + public ISyntaxInputBuilder GetBuilder(StateTableStore table, object key, bool trackIncrementalSteps, string? name, IEqualityComparer comparer) => new Builder(this, key, table, trackIncrementalSteps, name, comparer); private sealed class Builder : ISyntaxInputBuilder { diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs index 63b7be100b588..d969dc80a4c90 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverStrategy.cs @@ -27,7 +27,7 @@ public SyntaxReceiverStrategy( _syntaxHelper = syntaxHelper; } - public ISyntaxInputBuilder GetBuilder(StateTableStore table, object key, bool trackIncrementalSteps, string? name, IEqualityComparer? comparer) => new Builder(this, key, table, trackIncrementalSteps); + public ISyntaxInputBuilder GetBuilder(StateTableStore table, object key, bool trackIncrementalSteps, string? name, IEqualityComparer comparer) => new Builder(this, key, table, trackIncrementalSteps); private sealed class Builder : ISyntaxInputBuilder { From 8289e1f248861962a5283139ccdc915c154e70e0 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 15 Jan 2025 15:04:05 -0800 Subject: [PATCH 3/4] Cleanup comparers in nodes: All nodes either passed in a default comparer or null, which was stored in the node table. Modifications then had the option to supply a seperate comparer to do the modification. In all cases the comparer passed into the modify call was the same as the one passed when creating the table, so we can remove the call from modify and just use the table instance. Rather than each node passing it its own default when it doesn't have a comparer, just pass in null and let the table control creating the default. --- .../SourceGeneration/StateTableTests.cs | 26 +++++++++---------- .../SourceGeneration/Nodes/BatchNode.cs | 6 ++--- .../SourceGeneration/Nodes/CombineNode.cs | 2 +- .../SourceGeneration/Nodes/InputNode.cs | 6 ++--- .../SourceGeneration/Nodes/NodeStateTable.cs | 18 ++++++------- .../Nodes/PredicateSyntaxStrategy.cs | 6 ++--- .../Nodes/SourceOutputNode.cs | 6 ++--- .../SourceGeneration/Nodes/TransformNode.cs | 6 ++--- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/StateTableTests.cs b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/StateTableTests.cs index bb887e87ce6ec..e075bce56677d 100644 --- a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/StateTableTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/StateTableTests.cs @@ -82,9 +82,9 @@ public void Node_Builder_Can_Add_Entries_From_Previous_Table() var previousTable = builder.ToImmutableAndFree(); builder = previousTable.ToBuilder(stepName: null, false); - builder.TryModifyEntries(ImmutableArray.Create(10, 11), EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified); + builder.TryModifyEntries(ImmutableArray.Create(10, 11), TimeSpan.Zero, default, EntryState.Modified); builder.TryUseCachedEntries(TimeSpan.Zero, default, out var cachedEntries); // ((2, EntryState.Cached), (3, EntryState.Cached)) - builder.TryModifyEntries(ImmutableArray.Create(20, 21, 22), EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified); + builder.TryModifyEntries(ImmutableArray.Create(20, 21, 22), TimeSpan.Zero, default, EntryState.Modified); bool didRemoveEntries = builder.TryRemoveEntries(TimeSpan.Zero, default, out var removedEntries); //((6, EntryState.Removed)) var newTable = builder.ToImmutableAndFree(); @@ -185,9 +185,9 @@ public void Node_Builder_Handles_Modification_When_Both_Tables_Have_Empty_Entrie AssertTableEntries(previousTable, expected); builder = previousTable.ToBuilder(stepName: null, false); - Assert.True(builder.TryModifyEntries(ImmutableArray.Create(3, 2), EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); - Assert.True(builder.TryModifyEntries(ImmutableArray.Empty, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); - Assert.True(builder.TryModifyEntries(ImmutableArray.Create(3, 5), EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntries(ImmutableArray.Create(3, 2), TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntries(ImmutableArray.Empty, TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntries(ImmutableArray.Create(3, 5), TimeSpan.Zero, default, EntryState.Modified)); var newTable = builder.ToImmutableAndFree(); @@ -209,10 +209,10 @@ public void Node_Table_Doesnt_Modify_Single_Item_Multiple_Times_When_Same() AssertTableEntries(previousTable, expected); builder = previousTable.ToBuilder(stepName: null, false); - Assert.True(builder.TryModifyEntry(1, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); - Assert.True(builder.TryModifyEntry(2, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); - Assert.True(builder.TryModifyEntry(5, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); - Assert.True(builder.TryModifyEntry(4, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntry(1, TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntry(2, TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntry(5, TimeSpan.Zero, default, EntryState.Modified)); + Assert.True(builder.TryModifyEntry(4, TimeSpan.Zero, default, EntryState.Modified)); var newTable = builder.ToImmutableAndFree(); @@ -232,10 +232,10 @@ public void Node_Table_Caches_Previous_Object_When_Modification_Considered_Cache var expected = ImmutableArray.Create((1, EntryState.Added, 0), (2, EntryState.Added, 0), (3, EntryState.Added, 0)); AssertTableEntries(previousTable, expected); - builder = previousTable.ToBuilder(stepName: null, false); - Assert.True(builder.TryModifyEntry(1, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); // ((1, EntryState.Cached)) - Assert.True(builder.TryModifyEntry(4, EqualityComparer.Default, TimeSpan.Zero, default, EntryState.Modified)); // ((4, EntryState.Modified)) - Assert.True(builder.TryModifyEntry(5, new LambdaComparer((i, j) => true), TimeSpan.Zero, default, EntryState.Modified)); // ((3, EntryState.Cached)) + builder = previousTable.ToBuilder(stepName: null, false, new LambdaComparer((i, j) => i == 3 || i == j)); + Assert.True(builder.TryModifyEntry(1, TimeSpan.Zero, default, EntryState.Modified)); // ((1, EntryState.Cached)) + Assert.True(builder.TryModifyEntry(4, TimeSpan.Zero, default, EntryState.Modified)); // ((4, EntryState.Modified)) + Assert.True(builder.TryModifyEntry(5, TimeSpan.Zero, default, EntryState.Modified)); // ((3, EntryState.Cached)) var newTable = builder.ToImmutableAndFree(); expected = ImmutableArray.Create((1, EntryState.Cached, 0), (4, EntryState.Modified, 0), (3, EntryState.Cached, 0)); diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/BatchNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/BatchNode.cs index 3b803f961498b..f8c92b1791cde 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/BatchNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/BatchNode.cs @@ -16,13 +16,13 @@ internal sealed class BatchNode : IIncrementalGeneratorNode).FullName; private readonly IIncrementalGeneratorNode _sourceNode; - private readonly IEqualityComparer> _comparer; + private readonly IEqualityComparer>? _comparer; private readonly string? _name; public BatchNode(IIncrementalGeneratorNode sourceNode, IEqualityComparer>? comparer = null, string? name = null) { _sourceNode = sourceNode; - _comparer = comparer ?? EqualityComparer>.Default; + _comparer = comparer; _name = name; } @@ -136,7 +136,7 @@ public NodeStateTable> UpdateStateTable(DriverStateTable. } else if (!sourceTable.IsCached || !tableBuilder.TryUseCachedEntries(stopwatch.Elapsed, sourceInputs)) { - if (!tableBuilder.TryModifyEntry(sourceValues, _comparer, stopwatch.Elapsed, sourceInputs, EntryState.Modified)) + if (!tableBuilder.TryModifyEntry(sourceValues, stopwatch.Elapsed, sourceInputs, EntryState.Modified)) { tableBuilder.AddEntry(sourceValues, EntryState.Added, stopwatch.Elapsed, sourceInputs, EntryState.Added); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/CombineNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/CombineNode.cs index bd4c0d4c489c5..67795541805fb 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/CombineNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/CombineNode.cs @@ -72,7 +72,7 @@ public CombineNode(IIncrementalGeneratorNode input1, IIncrementalGenera }; var entry = (entry1.Item, input2); - if (state != EntryState.Modified || _comparer is null || !tableBuilder.TryModifyEntry(entry, _comparer, stopwatch.Elapsed, stepInputs, state)) + if (state != EntryState.Modified || _comparer is null || !tableBuilder.TryModifyEntry(entry, stopwatch.Elapsed, stepInputs, state)) { tableBuilder.AddEntry(entry, state, stopwatch.Elapsed, stepInputs, state); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs index 2e7205b08793e..66b8cfe04e012 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/InputNode.cs @@ -24,7 +24,7 @@ internal sealed class InputNode : IIncrementalGeneratorNode private readonly Func> _getInput; private readonly Action _registerOutput; private readonly IEqualityComparer _inputComparer; - private readonly IEqualityComparer _comparer; + private readonly IEqualityComparer? _comparer; private readonly string? _name; public InputNode(Func> getInput, IEqualityComparer? inputComparer = null) @@ -35,7 +35,7 @@ public InputNode(Func> getInput, IEq private InputNode(Func> getInput, Action? registerOutput, IEqualityComparer? inputComparer = null, IEqualityComparer? comparer = null, string? name = null) { _getInput = getInput; - _comparer = comparer ?? EqualityComparer.Default; + _comparer = comparer; _inputComparer = inputComparer ?? EqualityComparer.Default; _registerOutput = registerOutput ?? (o => throw ExceptionUtilities.Unreachable()); _name = name; @@ -83,7 +83,7 @@ public NodeStateTable UpdateStateTable(DriverStateTable.Builder graphState, N // This allows us to correctly 'replace' items even when they aren't actually the same. In the case that the // item really isn't modified, but a new item, we still function correctly as we mostly treat them the same, // but will perform an extra comparison that is omitted in the pure 'added' case. - var modified = tableBuilder.TryModifyEntry(inputItems[itemIndex], _comparer, elapsedTime, noInputStepsStepInfo, EntryState.Modified); + var modified = tableBuilder.TryModifyEntry(inputItems[itemIndex], elapsedTime, noInputStepsStepInfo, EntryState.Modified); Debug.Assert(modified); itemsSet.Remove(inputItems[itemIndex]); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs index 8758c9302a75a..31381a447f532 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs @@ -183,7 +183,7 @@ public NodeStateTable AsCached() public Builder ToBuilder(string? stepName, bool stepTrackingEnabled, IEqualityComparer? equalityComparer = null, int? tableCapacity = null) => new(this, stepName, stepTrackingEnabled, equalityComparer, tableCapacity); - public NodeStateTable CreateCachedTableWithUpdatedSteps(NodeStateTable inputTable, string? stepName, IEqualityComparer equalityComparer) + public NodeStateTable CreateCachedTableWithUpdatedSteps(NodeStateTable inputTable, string? stepName, IEqualityComparer? equalityComparer) { Debug.Assert(inputTable.HasTrackedSteps && inputTable.IsCached); NodeStateTable.Builder builder = ToBuilder(stepName, stepTrackingEnabled: true, equalityComparer); @@ -320,7 +320,7 @@ internal bool TryUseCachedEntries(TimeSpan elapsedTime, ImmutableArray<(Incremen return true; } - public bool TryModifyEntry(T value, IEqualityComparer comparer, TimeSpan elapsedTime, ImmutableArray<(IncrementalGeneratorRunStep InputStep, int OutputIndex)> stepInputs, EntryState overallInputState) + public bool TryModifyEntry(T value, TimeSpan elapsedTime, ImmutableArray<(IncrementalGeneratorRunStep InputStep, int OutputIndex)> stepInputs, EntryState overallInputState) { if (!TryGetPreviousEntry(out var previousEntry)) { @@ -335,13 +335,13 @@ public bool TryModifyEntry(T value, IEqualityComparer comparer, TimeSpan elap } Debug.Assert(previousEntry.Count == 1); - var (chosen, state, _) = GetModifiedItemAndState(previousEntry.GetItem(0), value, comparer); + var (chosen, state, _) = GetModifiedItemAndState(previousEntry.GetItem(0), value); _states.Add(new TableEntry(OneOrMany.Create(chosen), state)); RecordStepInfoForLastEntry(elapsedTime, stepInputs, overallInputState); return true; } - public bool TryModifyEntries(ImmutableArray outputs, IEqualityComparer comparer, TimeSpan elapsedTime, ImmutableArray<(IncrementalGeneratorRunStep InputStep, int OutputIndex)> stepInputs, EntryState overallInputState) + public bool TryModifyEntries(ImmutableArray outputs, TimeSpan elapsedTime, ImmutableArray<(IncrementalGeneratorRunStep InputStep, int OutputIndex)> stepInputs, EntryState overallInputState) { // Semantics: // For each item in the row, we compare with the new matching new value. @@ -384,7 +384,7 @@ public bool TryModifyEntries(ImmutableArray outputs, IEqualityComparer com var previousState = previousEntry.GetState(i); var replacementItem = outputs[i]; - var (chosenItem, state, chosePrevious) = GetModifiedItemAndState(previousItem, replacementItem, comparer); + var (chosenItem, state, chosePrevious) = GetModifiedItemAndState(previousItem, replacementItem); if (builder != null) { @@ -433,9 +433,9 @@ public bool TryModifyEntries(ImmutableArray outputs, IEqualityComparer com return true; } - public bool TryModifyEntries(ImmutableArray outputs, IEqualityComparer comparer, TimeSpan elapsedTime, ImmutableArray<(IncrementalGeneratorRunStep InputStep, int OutputIndex)> stepInputs, EntryState overallInputState, out TableEntry entry) + public bool TryModifyEntries(ImmutableArray outputs, TimeSpan elapsedTime, ImmutableArray<(IncrementalGeneratorRunStep InputStep, int OutputIndex)> stepInputs, EntryState overallInputState, out TableEntry entry) { - if (!TryModifyEntries(outputs, comparer, elapsedTime, stepInputs, overallInputState)) + if (!TryModifyEntries(outputs, elapsedTime, stepInputs, overallInputState)) { entry = default; return false; @@ -554,11 +554,11 @@ public NodeStateTable ToImmutableAndFree() isCached: finalStates.All(static s => s.IsCached) && _previous.GetTotalEntryItemCount() == finalStates.Sum(static s => s.Count)); } - private static (T chosen, EntryState state, bool chosePrevious) GetModifiedItemAndState(T previous, T replacement, IEqualityComparer comparer) + private (T chosen, EntryState state, bool chosePrevious) GetModifiedItemAndState(T previous, T replacement) { // when comparing an item to check if its modified we explicitly cache the *previous* item in the case where its // considered to be equal. This ensures that subsequent comparisons are stable across future generation passes. - return comparer.Equals(previous, replacement) + return _equalityComparer.Equals(previous, replacement) ? (previous, EntryState.Cached, chosePrevious: true) : (replacement, EntryState.Modified, chosePrevious: false); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs index 682ca1764f9cd..e6fc120ee48bd 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/PredicateSyntaxStrategy.cs @@ -48,7 +48,7 @@ public Builder(PredicateSyntaxStrategy owner, object key, StateTableStore tab _name = name; _comparer = comparer; _key = key; - _filterTable = table.GetStateTableOrEmpty(_owner._filterKey).ToBuilder(stepName: null, trackIncrementalSteps); + _filterTable = table.GetStateTableOrEmpty(_owner._filterKey).ToBuilder(stepName: null, trackIncrementalSteps, equalityComparer: Roslyn.Utilities.ReferenceEqualityComparer.Instance); _transformTable = table.GetStateTableOrEmpty(_key).ToBuilder(_name, trackIncrementalSteps, _comparer); } @@ -85,7 +85,7 @@ public void VisitTree( var stopwatch = SharedStopwatch.StartNew(); var nodes = getFilteredNodes(root.Value, _owner._filterFunc, cancellationToken); - if (state != EntryState.Modified || !_filterTable.TryModifyEntries(nodes, Roslyn.Utilities.ReferenceEqualityComparer.Instance, stopwatch.Elapsed, noInputStepsStepInfo, state, out entry)) + if (state != EntryState.Modified || !_filterTable.TryModifyEntries(nodes, stopwatch.Elapsed, noInputStepsStepInfo, state, out entry)) { entry = _filterTable.AddEntries(nodes, state, stopwatch.Elapsed, noInputStepsStepInfo, state); } @@ -108,7 +108,7 @@ public void VisitTree( // so we never consider the input to the transform as cached. var transformInputState = state == EntryState.Cached ? EntryState.Modified : state; - if (transformInputState == EntryState.Added || !_transformTable.TryModifyEntry(transformed, _comparer, stopwatch.Elapsed, noInputStepsStepInfo, transformInputState)) + if (transformInputState == EntryState.Added || !_transformTable.TryModifyEntry(transformed, stopwatch.Elapsed, noInputStepsStepInfo, transformInputState)) { _transformTable.AddEntry(transformed, EntryState.Added, stopwatch.Elapsed, noInputStepsStepInfo, EntryState.Added); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs index 79a71f0e2e17a..028a4764d3c66 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs @@ -46,12 +46,12 @@ public NodeStateTable UpdateStateTable(DriverStateTable.Builder graphSt this.LogTables(stepName, s_tableType, previousTable, previousTable, sourceTable); if (graphState.DriverState.TrackIncrementalSteps) { - return previousTable.CreateCachedTableWithUpdatedSteps(sourceTable, stepName, EqualityComparer.Default); + return previousTable.CreateCachedTableWithUpdatedSteps(sourceTable, stepName, equalityComparer: null); } return previousTable; } - var tableBuilder = graphState.CreateTableBuilder(previousTable, stepName, EqualityComparer.Default); + var tableBuilder = graphState.CreateTableBuilder(previousTable, stepName, equalityComparer: null); foreach (var entry in sourceTable) { var inputs = tableBuilder.TrackIncrementalSteps ? ImmutableArray.Create((entry.Step!, entry.OutputIndex)) : default; @@ -71,7 +71,7 @@ public NodeStateTable UpdateStateTable(DriverStateTable.Builder graphSt _action(context, entry.Item, cancellationToken); var sourcesAndDiagnostics = (sourcesBuilder.ToImmutable(), diagnostics.ToReadOnly()); - if (entry.State != EntryState.Modified || !tableBuilder.TryModifyEntry(sourcesAndDiagnostics, EqualityComparer.Default, stopwatch.Elapsed, inputs, entry.State)) + if (entry.State != EntryState.Modified || !tableBuilder.TryModifyEntry(sourcesAndDiagnostics, stopwatch.Elapsed, inputs, entry.State)) { tableBuilder.AddEntry(sourcesAndDiagnostics, EntryState.Added, stopwatch.Elapsed, inputs, EntryState.Added); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/TransformNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/TransformNode.cs index d63edba0ce260..1ff2adb1f2640 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/TransformNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/TransformNode.cs @@ -18,7 +18,7 @@ internal sealed class TransformNode : IIncrementalGeneratorNode private static readonly string? s_tableType = typeof(TOutput).FullName; private readonly Func> _func; - private readonly IEqualityComparer _comparer; + private readonly IEqualityComparer? _comparer; private readonly IIncrementalGeneratorNode _sourceNode; private readonly string? _name; private readonly bool _wrapUserFunc; @@ -33,7 +33,7 @@ public TransformNode(IIncrementalGeneratorNode sourceNode, Func.Default; + _comparer = comparer; _name = name; } @@ -87,7 +87,7 @@ public NodeStateTable UpdateStateTable(DriverStateTable.Builder builder throw new UserFunctionException(e); } - if (entry.State != EntryState.Modified || !tableBuilder.TryModifyEntries(newOutputs, _comparer, stopwatch.Elapsed, inputs, entry.State)) + if (entry.State != EntryState.Modified || !tableBuilder.TryModifyEntries(newOutputs, stopwatch.Elapsed, inputs, entry.State)) { tableBuilder.AddEntries(newOutputs, EntryState.Added, stopwatch.Elapsed, inputs, entry.State); } From 1d077d9a442870db8709548e41bfd24d336e9437 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 15 Jan 2025 15:56:08 -0800 Subject: [PATCH 4/4] Use a wrapped comparer as the default comparer --- .../Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs | 2 +- src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs index 31381a447f532..d166d64ec62ff 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs @@ -256,7 +256,7 @@ internal Builder( _states = ArrayBuilder.GetInstance(tableCapacity ?? previous.GetTotalEntryItemCount()); _previous = previous; _name = name; - _equalityComparer = equalityComparer ?? EqualityComparer.Default; + _equalityComparer = equalityComparer ?? WrappedUserComparer.Default; if (stepTrackingEnabled) { _steps = ArrayBuilder.GetInstance(); diff --git a/src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs b/src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs index 6aedc61bc6130..64d1588f2955e 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs @@ -25,6 +25,8 @@ internal sealed class WrappedUserComparer : IEqualityComparer { private readonly IEqualityComparer _inner; + public static WrappedUserComparer Default { get; } = new WrappedUserComparer(EqualityComparer.Default); + public WrappedUserComparer(IEqualityComparer inner) { _inner = inner;