Skip to content

Fix unpacking first-class module in default argument of react component#8296

Merged
cknitt merged 2 commits intomasterfrom
fix-unpacking
Mar 13, 2026
Merged

Fix unpacking first-class module in default argument of react component#8296
cknitt merged 2 commits intomasterfrom
fix-unpacking

Conversation

@cknitt
Copy link
Member

@cknitt cknitt commented Mar 13, 2026

Fixes #7917

PR Description: Fix module unpacking in default React props

Summary

This change fixes React component arguments that both:

  • use a default value, and
  • destructure a first-class module in the parameter pattern

The motivating case is:

module M = {
  @react.component
  let make = () => React.null
}

module type S = module type of M

@react.component
let make = (~component as module(C: S)=module(M: S)) => <C />

Before this change, the JSX PPX lowered defaulted props in a way that only worked reliably for simple variable patterns. First-class module unpacking patterns need to happen after the optional prop has already been resolved to a plain value.

Problem

For defaulted props, the PPX needs to transform:

~component as module(C: S)=...

into the internal props-record form used by React components.

The old lowering coupled optional-prop handling too closely to the original pattern shape. That works for identifiers, but not for module-unpack patterns, because unpacking module(C: S) must happen in a standalone let pattern over a resolved value, not inside the option-handling step.

Fix

The fix makes defaulted props go through a uniform two-step lowering:

  1. Bind the prop record field to a temporary __<label>.
  2. Resolve the optional prop to __<label>_value.
  3. Destructure the original pattern from that resolved value in a separate let.

Conceptually, the generated code now looks like:

let __component_value = switch __component {
| Some(component) => component
| None => module(M: S)
}

let module(C: S) = __component_value
<C />

This preserves the existing behavior for simple defaulted props while making first-class module unpacking work correctly.

Tests

The change is covered at two levels:

  • syntax snapshot coverage in
    tests/syntax_tests/data/ppx/react/defaultPatternProp.res
  • end-to-end build coverage in
    tests/build_tests/react_ppx/src/issue_7917_test.res

Why this shape

This approach is simpler than special-casing only variable-like patterns:

  • it uses the same lowering strategy for all defaulted props
  • it keeps the record-pattern binding phase and the destructuring phase separate
  • it naturally supports nontrivial patterns such as first-class module unpacking

@cknitt cknitt changed the title Fix unpacking first-class module in default argument of react Fix unpacking first-class module in default argument of react component Mar 13, 2026
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 13, 2026

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8296

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@8296

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@8296

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@8296

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@8296

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8296

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@8296

commit: 4fe7802

@cknitt cknitt requested a review from cristianoc March 13, 2026 13:37
Copy link
Collaborator

@cristianoc cristianoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes generated code for non-first-class-module cases, but seems legit to be consistent.

@cknitt
Copy link
Member Author

cknitt commented Mar 13, 2026

This changes generated code for non-first-class-module cases, but seems legit to be consistent.

Yes, I did have a version with more special cases before, but didn't quite like it.

@cknitt cknitt merged commit 55a50a9 into master Mar 13, 2026
25 checks passed
@cknitt cknitt deleted the fix-unpacking branch March 13, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unpacking first-class module directly in default argument of react component fails

2 participants