-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incremental Generator Work Tracking APIs #54832
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
@chsienki @sharwell @CyrusNajmabadi I decided to throw up an API proposal for the incremental generator testability that we chatted about a week or so ago to keep the conversation going. cc: @agocke |
Should we also include information about whether the step output changed? |
That seems reasonable. I think adding that depends on the way the step info is modeled. If we model it as the API proposal currently has, then we definitely should. However, if we move to a model that links the steps together, then it's less compelling since the next step's state would describe the previous step's output state. |
I've augmented the proposal to add output state tracking. |
I'd love to see this removed, and just get the results from |
I've updated the design to add the step data to the data structure returned by |
I've moved the run results to live on the |
…54832 and update incremental generator API tests to use it to validate incrementality.
My notes from API review: input, output -> should each be ImmutableArray
input state should be scalar. Even though there may be multiple inputs, we can combine the states (internally already done) output + state -> ImmutableArray<(,)> Track "Remove" Try to model step types as DU (either struct + state or inheritance model) Look at making a graph model: Internally is a graph, so is desirable to expose. Would also enable graphviz visualizations of generator runs.
Can make ExecutedSteps: named nodes should be the only nodes included. Give input nodes well-known names Don't worry about name collisions. That's the user's problem to handle if they conflict names. |
InputSources vs Inputs? Are the IncrementalGeneratorRunStepState names good? Output documents have an implicit name Should we just have a property input/output for the start and end of the graph, since they will always be there? What happens when a name is duplicated? Would We could add a TimeSpan to the IncrementalGeneratorStep We should investigate a diff mechanism for the testing SDK The only remaining open question is around the API design for the namespace Microsoft.CodeAnalysis
{
public static class IncrementalValueProviderExtensions
{
+ public static IncrementalValueProvider<TSource> WithTrackingName<TSource>(this IncrementalValueProvider<TSource> source, string name);
}
}
namespace Microsoft.CodeAnalysis
{
public readonly struct GeneratorDriverOptions
{
public readonly IncrementalGeneratorOutputKind DisabledOutputs;
+ public readonly bool TrackIncrementalGeneratorSteps;
public GeneratorDriverOptions(IncrementalGeneratorOutputKind disabledOutputs);
+ public GeneratorDriverOptions(IncrementalGeneratorOutputKind disabledOutputs, bool trackIncrementalGeneratorSteps);
}
public class GeneratorRunResult
{
+ public ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> TrackedSteps { get; }
}
+ public class IncrementalGeneratorRunStep
+ {
+ ... const strings of the well-known step names
+ public string? Name { get; }
+ public ImmutableArray<(IncrementalGeneratorRunStep Source, int OutputIndex)> Inputs { get; }
+ public ImmutableArray<(object Value, IncrementalGeneratorRunStepState State)> Outputs { get; }
+ public TimeSpan ElapsedTime { get; }
+ }
+ public enum IncrementalGeneratorRunStepState
+ {
+ New,
+ Modified,
+ Unchanged,
+ Cached,
+ Removed
+ }
} |
Approved with the following shape after finishing review over email: namespace Microsoft.CodeAnalysis
{
public static class IncrementalValueProviderExtensions
{
+ public static IncrementalValueProvider<TSource> WithTrackingName<TSource>(this IncrementalValueProvider<TSource> source, string name);
+ public static IncrementalValuesProvider<TSource> WithTrackingName<TSource>(this IncrementalValuesProvider<TSource> source, string name);
}
public readonly struct GeneratorDriverOptions
{
+ public readonly bool TrackIncrementalGeneratorSteps;
+ public GeneratorDriverOptions(IncrementalGeneratorOutputKind disabledOutputs, bool trackIncrementalGeneratorSteps);
}
public class GeneratorRunResult
{
+ public ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> TrackedSteps { get; }
+ public ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> TrackedOutputSteps { get; }
}
+ public static class WellKnownGeneratorInputs
+ {
+ public const string Compilation = nameof(Compilation);
+
+ public const string MetadataReferences = nameof(MetadataReferences);
+
+ public const string AdditionalTexts = nameof(AdditionalTexts);
+
+ public const string ParseOptions = nameof(ParseOptions);
+
+ public const string AnalyzerConfigOptions = nameof(AnalyzerConfigOptions);
+
+ public const string SyntaxTrees = nameof(SyntaxTrees);
+ }
+ public static class WellKnownGeneratorOutputs
+ {
+ public const string SourceOutput = nameof(SourceOutput);
+
+ public const string ImplementationSourceOutput = nameof(ImplementationSourceOutput);
+ }
+ public class IncrementalGeneratorRunStep
+ {
+ public string? Name { get; }
+ public ImmutableArray<(IncrementalGeneratorRunStep Source, int OutputIndex)> Inputs { get; }
+ public ImmutableArray<(object Value, IncrementalStepRunReason Reason)> Outputs { get; }
+ public TimeSpan ElapsedTime { get; }
+ }
+ public enum IncrementalStepRunReason
+ {
+ New,
+ Modified,
+ Unchanged,
+ Cached,
+ Removed
+ }
} |
Background and Motivation
The Source Generator V2 APIs introduce the concept of "incremental generators". These generators describe their work as a pipeline of transformations and filters, which the generator driver can incrementally run steps of a generator. As part of supporting this, the API allows developers to provide custom comparers to enable using their own custom types between stages. For complex source generators that carry a lot of state, it is desirable to validate that the incrementality for various steps is preserved (a given step in the transform will result in the same value as a prior run, so later results based on this result are pulled from the cache).
This proposal proposes APIs to enable tracking this information without requiring incremental generators to carry state (which is undesired by the Roslyn team to my understanding).
Approved API
Proposed API (Updated based on August 25, 2021 API Review)
Generators created with a
GeneratorDriverOptions
instance withTrackIncrementalGeneratorSteps=true
would record the results of each of the steps of the various incremental generators run by the driver.Open Question: Should the recorded steps include unnamed steps and named steps, or just named steps?
Usage Examples
Alternative Designs
The step info could instead be designed as a linked list of step status through the generator chain instead of an array with no well-defined ordering. This design would make it easier to associate two steps together, but provides difficulties around excluding unnamed steps. Additionally, this design may have to "leak" implementation details about how the steps are chained together to efficiently construct the list.
Risks
As the rules around when a generator considers a change a "modify" change vs a "remove/add" change are not well-defined, the determination around how they are codified in this API may risk causing breaking changes in the future if the rules change.
The text was updated successfully, but these errors were encountered: