Skip to content

Decide [ArgumentsSource] element-to-parameter mapping from the declared type - #3265

Merged
timcassell merged 2 commits into
masterfrom
feature/arguments-source-element-matching
Sep 20, 2026
Merged

timcassell merged 2 commits into
masterfrom
feature/arguments-source-element-matching

Conversation

@timcassell

@timcassell timcassell commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

IEnumerable<object[]> -> IEnumerable<(int, string)>
IEnumerable<object> -> IEnumerable<long>

Core changes:

  1. The element-to-parameter mapping is decided from the declared element type.
    • On master two independent decisions disagreed: SmartParamBuilder.Indexes decided whether the generated code indexes a row from the source's declared return type, while CreateForArguments decided what the in-process toolchains pass from the row's runtime shape. Wherever they disagreed the two toolchains took different arguments out of the same row — one shape threw InvalidCastException at run time, another failed silently.
    • A benchmark taking one argument accepts a source that yields elements of the exact type (or convertible to by-ref-like), or object for back-compat.
    • A benchmark taking multiple arguments accepts a source that yields ValueTuple containing the respective parameter types, each type matching exactly (or convertible to by-ref-like), or object[] for back-compat.

Behavior changes:

  1. Added explicit conversion operator support for by-ref-like parameters, in both toolchains.
    • On master the generated code relied on the assignment applying the conversion, where C# applies only an implicit operator, and the in-process emitter looked up op_Implicit alone. Discovery admitted the declaration and the failure landed later as a compiler error or Bug: No conversion from X to Y.
    • The same lookup reads [Params]/[ParamsSource] values, so explicit operators now work there too. ParamSourceTests's two tests become the single theory over both toolchains its own comment said they should become.
  2. A value of a type derived from the declared one is no longer refused.
    • The conversion was looked for on value.GetType(), and an operator is declared for exactly one type, so a derived value was refused by the very rule its declaration had passed.
  3. A null value can feed a by-ref-like parameter, where the source names the type it converts from.
    • On master a null named no type, so it skipped the conversion check and reached a field the generated code cannot declare — CS8345 out of process, cannot store as a class field in the emitter. It now travels the declaration like any other value: IEnumerable<byte[]?> yielding null feeds ReadOnlySpan<byte> the empty span.
  4. A null the parameter cannot hold is refused where the declaration named no type.
    • That is an attribute's argument and a source declared to yield object. Everywhere else the declared element type was judged already — and a null read from a source that names its type says the value could not be read rather than that it was null, since an IEnumerable<T> where T : allows ref struct cannot box one to be looked at.
    • The toolchains disagreed about such a value. [Arguments(null)] for an int parameter ran in-process against a silently substituted 0 and failed out of process with CS0037; for a ref struct parameter it was CS8345 instead. Both now stop at discovery, naming the origin, the parameter and its type.
    • Null remains a value for any reference type and for Nullable<T>, so [Arguments(null)] on a string or int? parameter is untouched.
  5. Support string to ReadOnlySpan<Char> in .Net Framework, like the C# first-class-spans feature.

Breaking changes:

  1. IEnumerable<object[]> feeding a single-parameter benchmark is refused (unless the parameter is itself object[] or convertible to by-ref-like like Span<object>).
    • A declaration cannot tell a one-element wrapper apart from an argument list for a benchmark that happens to take one argument, and the documentation has never shown the wrapper.

Other Changes:

  1. The analyzer assemblies are no longer signed, same as the weaver.
  2. Updated docs and samples.
  3. New analyzers and code fixers.
  4. New and updated tests.

…ed type

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@timcassell
timcassell merged commit 3f39bfb into master Sep 20, 2026
21 checks passed
@timcassell
timcassell deleted the feature/arguments-source-element-matching branch September 20, 2026 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant