Repository navigation
GH-4680: emit F# for the MultipartReader endpoint parameter - #4688
Merged
Merged
Conversation
Follow-up to #4674. ReadMultipartBody overrode GenerateCode only, so an F# endpoint taking a MultipartReader fell through to Frame's default-throwing seam: "F# code generation is not supported for the frame 'ReadMultipartBody'". The contributor disclosed this and was right about the precedent -- FromFileStrategy's frames have no emitter either, so it is not a regression from that PR. But the frame is otherwise a faithful copy of the C# JSON shape, and JsonHandling DOES have an emitter, so the asymmetry is worth closing. The one non-obvious part is the qualification. ReadJsonAsync is an inherited INSTANCE method, so the F# emitter writes `this.ReadJsonAsync` (#393). ReadMultipartAsync is static, so it is qualified by TYPE instead -- `Wolverine.Http.HttpHandler.ReadMultipartAsync(...)` -- exactly as WriteStringFrame already does for the static WriteString. The C# emit can call either one bare only because the generated handler derives from HttpHandler; F# resolves no such thing and a bare call is an FS0039. Everything else follows JsonHandling: the return is a struct tuple, so F# needs `let! struct (a, b) =`, and since F# has no early `return` the abort guard renders the rest of the chain in its `else` via FSharpEmitHelpers.WriteAbortGuard. Proven by compilation, not by string matching: a MultipartReader endpoint joins Wolverine.Http.FSharpContracts and its chain joins the codegen sample, so the existing compile gate regenerates Generated.fs and runs dotnet build over it. Reverting only the emitter fails 5 of the 6 tests with the NotSupportedException above, which also proves the new endpoint actually reaches this frame. Does not change the fsharp-coverage total's story -- see #4681, where the ratchet that would have caught this frame arriving unemitted lives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDUrBeB4tTnKj4AExCS1nj
jeremydmiller
added a commit
that referenced
this pull request
Sep 29, 2026
jeremydmiller
added a commit
that referenced
this pull request
Sep 29, 2026
ReadMultipartBody has an F# emitter as of #4680/#4688, so it leaves the remaining bucket: 54 -> 53. This is the ratchet doing exactly what it is for. Merging origin/main made the gate fail on its own terms, naming the frame and saying to strike it from the list rather than leaving a count mismatch to decipher. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDUrBeB4tTnKj4AExCS1nj
Member
Author
|
Released in Wolverine 6.42.0. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4680. Follow-up to #4674.
ReadMultipartBodyoverrodeGenerateCodeonly, so an F# endpoint taking aMultipartReaderfell through toFrame's default-throwing seam:The contributor disclosed this in the PR and was right about the precedent —
FromFileStrategy's frames have no emitter either, so this is not a regression that PR introduced. ButJsonHandlingdoes have one, and the new frame is otherwise a faithful copy of the C# JSON shape, so the asymmetry is worth closing.The one non-obvious part
Qualification, and the issue was right to flag it.
ReadJsonAsyncthis.ReadJsonAsync— #393ReadMultipartAsyncWolverine.Http.HttpHandler.ReadMultipartAsync(...)So it follows
WriteStringFrame, which already qualifies the staticWriteStringby type, rather thanJsonHandling. The C# emit can call either bare only because the generated handler derives fromHttpHandler; F# resolves no such thing and a bare call is an FS0039.Everything else follows
JsonHandling: the return is a struct tuple, so F# needslet! struct (a, b) =, and since F# has no earlyreturn, the abort guard renders the rest of the chain inside itselseviaFSharpEmitHelpers.WriteAbortGuard.Emitted output:
Proven by compilation, not string matching
A
MultipartReaderendpoint joinsWolverine.Http.FSharpContracts, and its chain joinsHttpFSharpCodegenSample, so the existing compile gate regeneratesGenerated.fsand shellsdotnet buildover the F# fixture. That is the acceptance test; the string assertion inmultipart_reader_qualifies_its_static_helper_by_typejust pins why it compiles, since getting the qualification wrong is the failure mode that would otherwise be caught only by a confusing compiler error.Reverting only the emitter fails 5 of 6 tests with the
NotSupportedExceptionabove — which also proves the new endpoint genuinely reaches this frame rather than the test passing vacuously.dotnet build wolverine.slnx -c Release -f net9.0: 0 warnings / 0 errors.Note on the alternative
The issue floats a startup assertion — "this parameter is not supported in F# endpoints" — as possibly the better first step. That is still worth doing for the frames that have no emitter, and it belongs with #4681 where the remaining bucket is managed; it just isn't needed for this frame any more.
Merge order with #4681
#4681 commits a baseline of the frames still lacking an emitter, and
ReadMultipartBodyis currently in it. Whichever of the two merges second needs that one line updated — I'll handle it.🤖 Generated with Claude Code
https://claude.ai/code/session_01VDUrBeB4tTnKj4AExCS1nj