Skip to content

[checking] Generate Contravariant and Profunctor derived members - #236

Merged
purefunctor merged 3 commits into
mainfrom
derive-contravariant-profunctor
Aug 2, 2026
Merged

[checking] Generate Contravariant and Profunctor derived members#236
purefunctor merged 3 commits into
mainfrom
derive-contravariant-profunctor

Conversation

@purefunctor

Copy link
Copy Markdown
Owner

Summary

Add generated instance members for the Contravariant and Profunctor class family.

The implementation keeps Contravariant/Profunctor emission in an independent generator while extending the shared variance recipe to represent covariant and contravariant application edges. It supports functions, records, unary applications, binary Bifunctor/Profunctor applications, phantom parameters, open records, and available instance constraints.

Functor and Bifunctor derivation can now traverse nested Contravariant and Profunctor edges, recovering covariance across negative boundaries. Foldable and Traversable retain their existing covariant-only traversal policy.

The commits preserve the mixed-variance regressions before applying the Functor/Bifunctor fix, making the generated semantic-tree changes directly reviewable.

Testing

  • cargo check -p checking --tests
  • cargo nextest run -p checking
  • just t checking
  • just t semantic
  • cargo clippy -p checking --tests -- -D warnings -A clippy::too_many_arguments
  • just format

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@purefunctor, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8e224acb-1f05-4c56-9193-37026cbc5be8

📥 Commits

Reviewing files that changed from the base of the PR and between ab369de and 35df850.

⛔ Files ignored due to path filters (31)
  • tests-integration/fixtures/checking/1772565240_derive_bifunctor_missing_functor/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1772567520_derive_functor_higher_kinded/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1772567580_derive_bifunctor_higher_kinded/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785319620_derive_bifunctor_captured_application_argument/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785319980_preserve_unary_variance_application_checks/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_captured_application_head/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_missing_traversal_instance/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_nested_parity_rejection/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_profunctor_bifunctor_priority_rejection/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_empty_data_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_function_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_functor_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_mixed_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_record_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_function_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_both_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_left_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_unary_preference_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598260_derive_profunctor_bifunctor_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_given_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_triple_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_given_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_nested_right_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_nullary_constructor_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_open_record_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_phantom_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598920_derive_contravariant_bifunctor_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598920_derive_profunctor_record_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785603960_derive_bifunctor_mixed_variance_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785603960_derive_functor_contravariant_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785603960_derive_functor_profunctor_application_body/Main.snap is excluded by !**/*.snap
📒 Files selected for processing (35)
  • compiler-core/checking/src/context.rs
  • compiler-core/checking/src/source/derive/contravariant.rs
  • compiler-core/checking/src/source/derive/generate.rs
  • compiler-core/checking/src/source/derive/generate/contravariant.rs
  • compiler-core/checking/src/source/derive/generate/foldable.rs
  • compiler-core/checking/src/source/derive/generate/functor.rs
  • compiler-core/checking/src/source/derive/generate/traversable.rs
  • compiler-core/checking/src/source/derive/member.rs
  • compiler-core/checking/src/source/derive/variance.rs
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_captured_application_head/Main.purs
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_missing_traversal_instance/Main.purs
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_nested_parity_rejection/Main.purs
  • tests-integration/fixtures/checking/1785598200_derive_profunctor_bifunctor_priority_rejection/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_empty_data_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_function_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_functor_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_mixed_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_record_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_function_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_both_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_left_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_unary_preference_body/Main.purs
  • tests-integration/fixtures/semantic/1785598260_derive_profunctor_bifunctor_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_given_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_triple_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_given_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_nested_right_body/Main.purs
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_nullary_constructor_body/Main.purs
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_open_record_body/Main.purs
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_phantom_body/Main.purs
  • tests-integration/fixtures/semantic/1785598920_derive_contravariant_bifunctor_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598920_derive_profunctor_record_body/Main.purs
  • tests-integration/fixtures/semantic/1785603960_derive_bifunctor_mixed_variance_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785603960_derive_functor_contravariant_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785603960_derive_functor_profunctor_application_body/Main.purs

Summary

  • Add generated instance members for Contravariant and Profunctor.
  • Extend variance analysis for covariant and contravariant application edges.
  • Support functions, records, unary and binary applications, phantom parameters, open records, and instance constraints.
  • Allow Functor and Bifunctor derivation to cross nested negative variance edges.
  • Keep Foldable and Traversable traversal covariant-only.
  • Add integration fixtures for valid and rejected mixed-variance cases.

Testing

  • Run checking, nextest, project test suites, clippy, and formatting checks.

Confidence 4/5 - Broad coverage with complex variance logic

The implementation has extensive fixture coverage across supported constructors and variance combinations. The main merge risk is the complexity of the new contravariant and profunctor traversal generator, especially around nested applications and constraint resolution.

Walkthrough

The compiler now supports variance-aware derivation for Contravariant and Profunctor. It collects cmap and dimap, selects mixed variance traversals, generates members for functions, applications, constructors, and records, and adds checking and semantic fixtures.

Changes

Variance-aware derivation

Layer / File(s) Summary
Variance analysis and instance selection
compiler-core/checking/src/source/derive/variance.rs, compiler-core/checking/src/source/derive/member.rs
Traversal recipes now record variance. Mixed applications select and validate Bifunctor, Profunctor, Functor, or Contravariant instances.
Known terms and derivation dispatch
compiler-core/checking/src/context.rs, compiler-core/checking/src/source/derive/generate.rs, compiler-core/checking/src/source/derive/contravariant.rs
KnownTermsCore now stores cmap and dimap. Known-instance generation dispatches Contravariant and Profunctor to the new generator.
Contravariant and Profunctor member generation
compiler-core/checking/src/source/derive/generate/contravariant.rs, compiler-core/checking/src/source/derive/generate/functor.rs, compiler-core/checking/src/source/derive/generate/foldable.rs, compiler-core/checking/src/source/derive/generate/traversable.rs
Generation now handles variance reversal across functions, unary and binary applications, constructors, and records. It selects map, cmap, bimap, or dimap.
Derivation integration fixtures
tests-integration/fixtures/checking/*1785598200*, tests-integration/fixtures/semantic/*1785598200*, tests-integration/fixtures/semantic/*1785598260*, tests-integration/fixtures/semantic/*1785598320*, tests-integration/fixtures/semantic/*1785598440*, tests-integration/fixtures/semantic/*1785598920*, tests-integration/fixtures/semantic/*1785603960*
Fixtures cover valid and rejected derivations for functions, records, constructors, nested applications, constraints, phantom parameters, and mixed variance.

Sequence Diagram(s)

sequenceDiagram
  participant DeriveMember
  participant VarianceChecker
  participant InstanceGenerator
  participant KnownTermsCore
  DeriveMember->>VarianceChecker: build variance recipe
  VarianceChecker->>InstanceGenerator: pass traversal recipe
  InstanceGenerator->>KnownTermsCore: resolve cmap or dimap
  InstanceGenerator-->>DeriveMember: return generated instance member
Loading
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description directly matches the changes, including Contravariant and Profunctor generation, mixed-variance traversal, and testing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.84291% with 150 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.60%. Comparing base (7849883) to head (35df850).

Files with missing lines Patch % Lines
...ecking/src/source/derive/generate/contravariant.rs 78.63% 55 Missing and 39 partials ⚠️
...mpiler-core/checking/src/source/derive/variance.rs 82.95% 24 Missing and 21 partials ⚠️
...ore/checking/src/source/derive/generate/functor.rs 83.33% 0 Missing and 7 partials ⚠️
compiler-core/checking/src/context.rs 50.00% 0 Missing and 2 partials ⚠️
...mpiler-core/checking/src/source/derive/generate.rs 92.30% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #236      +/-   ##
==========================================
- Coverage   83.67%   83.60%   -0.08%     
==========================================
  Files         159      160       +1     
  Lines       35439    36102     +663     
  Branches    35439    36102     +663     
==========================================
+ Hits        29655    30183     +528     
- Misses       3131     3208      +77     
- Partials     2653     2711      +58     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (6)
compiler-core/checking/src/source/derive/generate/foldable.rs (2)

534-534: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider matching the covariant variance explicitly instead of ignoring it.

Fold derivation is only reachable with FunctionPolicy::Reject, so the recorded variance is always Variance::Covariant today. The .. pattern hides that assumption. If a fold-style configuration later allows function fields, the mixed path would record a contravariant edge and this code would fold across it silently. Matching argument_variance: Variance::Covariant and returning Ok(None) for the contravariant case would make the invariant explicit.

As per coding guidelines: "Investigate architectural root faults, avoid escape hatches and temporary fixes, and use the type system to encode correctness."

Also applies to: 586-586

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@compiler-core/checking/src/source/derive/generate/foldable.rs` at line 534,
Update the TraversalOperation::UnaryApplication match in the fold derivation
logic to bind argument_variance and accept only Variance::Covariant; return
Ok(None) for contravariant edges so unsupported variance cannot be folded
silently. Apply the same explicit variance handling to the corresponding match
around the second referenced location.

Source: Coding guidelines


534-586: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Covariant-only variance is assumed implicitly in the fold and traverse emitters. Both emitters now discard the new argument_variance and first_variance fields with ... The assumption holds only because Foldable, Bifoldable, Traversable, and Bitraversable all configure FunctionPolicy::Reject, which prevents supports_contravariant_traversal from selecting the mixed path. That link is not visible at either call site.

  • compiler-core/checking/src/source/derive/generate/foldable.rs#L534-L586: match argument_variance: Variance::Covariant and first_variance: Variance::Covariant, and return Ok(None) for the contravariant case.
  • compiler-core/checking/src/source/derive/generate/traversable.rs#L632-L701: apply the same explicit match in emit_effect.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@compiler-core/checking/src/source/derive/generate/foldable.rs` around lines
534 - 586, Make variance handling explicit in foldable.rs at lines 534-586 and
traversable.rs at lines 632-701: in the relevant operation matches, accept only
argument_variance: Variance::Covariant and first_variance: Variance::Covariant,
returning Ok(None) for contravariant cases. Update foldable.rs and
traversable.rs consistently; no other sites require changes.
compiler-core/checking/src/source/derive/generate/traversable.rs (1)

632-632: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider matching the covariant variance explicitly instead of ignoring it.

Traversable and Bitraversable use FunctionPolicy::Reject, so the recorded variance is always Variance::Covariant. The .. pattern hides that assumption, in the same way as compiler-core/checking/src/source/derive/generate/foldable.rs.

Also applies to: 701-701

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@compiler-core/checking/src/source/derive/generate/traversable.rs` at line
632, Update the TraversalOperation::UnaryApplication matches in the traversable
generation logic to bind or explicitly match the variance as Variance::Covariant
instead of ignoring it with `..`; apply the same change at both referenced match
sites while preserving the existing FunctionPolicy::Reject behavior.
compiler-core/checking/src/source/derive/variance.rs (2)

824-834: 🚀 Performance & Scalability | 🔵 Trivial

Consider caching the elaborated available constraints.

has_instance_head canonicalises and elaborates the whole available_constraints list on every call. check_mixed_application can call it up to four times per type application, and the checker visits every field of every constructor. The elaborated set does not change during a single derivation, so computing it once per VarianceFieldChecker would remove repeated work.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@compiler-core/checking/src/source/derive/variance.rs` around lines 824 - 834,
The has_instance_head path currently canonicalises and elaborates
available_constraints on every call, causing repeated work during a derivation.
Cache the elaborated canonical constraints as state owned by
VarianceFieldChecker, initialise it once per checker, and have has_instance_head
reuse that cached result while preserving the existing filtering and error
propagation behavior.

737-758: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider removing the unreachable! by restructuring the guard.

The condition already tests second.is_some(), so the else branch cannot run. The type system can encode this instead of a runtime panic. Match on (first, second) before the instance lookup, or bind second with if let Some(second) = second as the first term of the chain.

♻️ Suggested restructure
-        if first.is_none()
-            && second.is_some()
+        if first.is_none()
+            && let Some(second_operation) = second.as_ref()
             && let Some(functor) = self.context.known_types.functor
             && has_instance_head(
                 self.state,
                 self.context,
                 self.available_constraints,
                 functor,
                 application.function,
             )?
         {
             if *self.valid {
                 tools::emit_constraint(self.context, self.state, functor, application.function);
             }
-            let Some(second) = second else {
-                unreachable!("right-only traversal requires a second operation")
-            };
             return Ok(Some(TraversalOperation::UnaryApplication {
                 argument_variance: Variance::Covariant,
-                argument: Box::new(second),
+                argument: Box::new(second_operation.clone()),
             }));
         }

The clone can be avoided by restructuring the match order if TraversalOperation is not Clone.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@compiler-core/checking/src/source/derive/variance.rs` around lines 737 - 758,
Restructure the unary-application guard around the relevant traversal logic so
`second` is bound as `Some` before the functor instance lookup, eliminating the
redundant `unreachable!` branch. Preserve the existing constraint emission and
`TraversalOperation::UnaryApplication` construction, and avoid cloning the
traversal operation if possible.
compiler-core/checking/src/source/derive/generate/contravariant.rs (1)

460-644: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Share the duplicated emitter helpers with generate/functor.rs.

TraversalContext, emit_transformer, emit_identity, emit_record_traversal, and extract_record_row are identical to their counterparts in compiler-core/checking/src/source/derive/generate/functor.rs. emit_binary also matches functor.rs::emit_binary_application apart from the documentation comments. Any future fix to variance orientation or record reconstruction must be applied twice, and the two copies can drift.

Extract the shared pieces into a common module, for example generate/traversal_context.rs, or define a small trait over the mapping lookup so both emitters reuse one implementation. Keep the Function arm specific to the contravariant emitter, because that arm is the genuine difference between the two.

As per coding guidelines: "In Rust code, avoid abstractions for their own sake; introduce them when they improve clarity, reduce real complexity, or simplify repeated work."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@compiler-core/checking/src/source/derive/generate/contravariant.rs` around
lines 460 - 644, Extract the duplicated traversal helpers—TraversalContext,
emit_transformer, emit_identity, emit_record_traversal, extract_record_row, and
the shared binary-emission logic—into a common generate module reusable by both
the contravariant emitter and functor.rs. Update both emitters to call the
shared implementation while retaining the contravariant emitter’s
Function-specific arm and variance behavior where it genuinely differs.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@compiler-core/checking/src/source/derive/generate/contravariant.rs`:
- Around line 460-644: Extract the duplicated traversal
helpers—TraversalContext, emit_transformer, emit_identity,
emit_record_traversal, extract_record_row, and the shared binary-emission
logic—into a common generate module reusable by both the contravariant emitter
and functor.rs. Update both emitters to call the shared implementation while
retaining the contravariant emitter’s Function-specific arm and variance
behavior where it genuinely differs.

In `@compiler-core/checking/src/source/derive/generate/foldable.rs`:
- Line 534: Update the TraversalOperation::UnaryApplication match in the fold
derivation logic to bind argument_variance and accept only Variance::Covariant;
return Ok(None) for contravariant edges so unsupported variance cannot be folded
silently. Apply the same explicit variance handling to the corresponding match
around the second referenced location.
- Around line 534-586: Make variance handling explicit in foldable.rs at lines
534-586 and traversable.rs at lines 632-701: in the relevant operation matches,
accept only argument_variance: Variance::Covariant and first_variance:
Variance::Covariant, returning Ok(None) for contravariant cases. Update
foldable.rs and traversable.rs consistently; no other sites require changes.

In `@compiler-core/checking/src/source/derive/generate/traversable.rs`:
- Line 632: Update the TraversalOperation::UnaryApplication matches in the
traversable generation logic to bind or explicitly match the variance as
Variance::Covariant instead of ignoring it with `..`; apply the same change at
both referenced match sites while preserving the existing FunctionPolicy::Reject
behavior.

In `@compiler-core/checking/src/source/derive/variance.rs`:
- Around line 824-834: The has_instance_head path currently canonicalises and
elaborates available_constraints on every call, causing repeated work during a
derivation. Cache the elaborated canonical constraints as state owned by
VarianceFieldChecker, initialise it once per checker, and have has_instance_head
reuse that cached result while preserving the existing filtering and error
propagation behavior.
- Around line 737-758: Restructure the unary-application guard around the
relevant traversal logic so `second` is bound as `Some` before the functor
instance lookup, eliminating the redundant `unreachable!` branch. Preserve the
existing constraint emission and `TraversalOperation::UnaryApplication`
construction, and avoid cloning the traversal operation if possible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ee9b676e-1c90-40ea-9a5b-dbb2117b1dc3

📥 Commits

Reviewing files that changed from the base of the PR and between 7849883 and ab369de.

⛔ Files ignored due to path filters (31)
  • tests-integration/fixtures/checking/1772565240_derive_bifunctor_missing_functor/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1772567520_derive_functor_higher_kinded/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1772567580_derive_bifunctor_higher_kinded/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785319620_derive_bifunctor_captured_application_argument/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785319980_preserve_unary_variance_application_checks/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_captured_application_head/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_missing_traversal_instance/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_nested_parity_rejection/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/checking/1785598200_derive_profunctor_bifunctor_priority_rejection/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_empty_data_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_function_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_functor_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_mixed_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_record_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_function_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_both_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_left_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_unary_preference_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598260_derive_profunctor_bifunctor_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_given_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_triple_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_given_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_nested_right_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_nullary_constructor_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_open_record_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_phantom_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598920_derive_contravariant_bifunctor_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785598920_derive_profunctor_record_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785603960_derive_bifunctor_mixed_variance_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785603960_derive_functor_contravariant_application_body/Main.snap is excluded by !**/*.snap
  • tests-integration/fixtures/semantic/1785603960_derive_functor_profunctor_application_body/Main.snap is excluded by !**/*.snap
📒 Files selected for processing (35)
  • compiler-core/checking/src/context.rs
  • compiler-core/checking/src/source/derive/contravariant.rs
  • compiler-core/checking/src/source/derive/generate.rs
  • compiler-core/checking/src/source/derive/generate/contravariant.rs
  • compiler-core/checking/src/source/derive/generate/foldable.rs
  • compiler-core/checking/src/source/derive/generate/functor.rs
  • compiler-core/checking/src/source/derive/generate/traversable.rs
  • compiler-core/checking/src/source/derive/member.rs
  • compiler-core/checking/src/source/derive/variance.rs
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_captured_application_head/Main.purs
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_missing_traversal_instance/Main.purs
  • tests-integration/fixtures/checking/1785598200_derive_contravariant_nested_parity_rejection/Main.purs
  • tests-integration/fixtures/checking/1785598200_derive_profunctor_bifunctor_priority_rejection/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_empty_data_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_function_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_functor_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_mixed_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_contravariant_record_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_function_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_both_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_nested_left_body/Main.purs
  • tests-integration/fixtures/semantic/1785598200_derive_profunctor_unary_preference_body/Main.purs
  • tests-integration/fixtures/semantic/1785598260_derive_profunctor_bifunctor_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_given_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_contravariant_triple_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_given_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598320_derive_profunctor_nested_right_body/Main.purs
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_nullary_constructor_body/Main.purs
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_open_record_body/Main.purs
  • tests-integration/fixtures/semantic/1785598440_derive_contravariant_phantom_body/Main.purs
  • tests-integration/fixtures/semantic/1785598920_derive_contravariant_bifunctor_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785598920_derive_profunctor_record_body/Main.purs
  • tests-integration/fixtures/semantic/1785603960_derive_bifunctor_mixed_variance_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785603960_derive_functor_contravariant_application_body/Main.purs
  • tests-integration/fixtures/semantic/1785603960_derive_functor_profunctor_application_body/Main.purs

@purefunctor
purefunctor force-pushed the derive-contravariant-profunctor branch from ab369de to 511058a Compare August 2, 2026 04:30
@purefunctor
purefunctor force-pushed the derive-contravariant-profunctor branch from 511058a to 35df850 Compare August 2, 2026 04:30
@purefunctor
purefunctor merged commit 1dcec0b into main Aug 2, 2026
13 of 15 checks passed
@purefunctor
purefunctor deleted the derive-contravariant-profunctor branch August 2, 2026 04:45
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.

1 participant