Skip to main content
v0.14.0

RFC-0123 field-wise row constraints were incomplete

Limitation​

where all R: Aspect parses (all is contextual, not reserved) and is enforced where it is written: on an extend whose target is { ..R } (the impl applies to a record only when every field satisfies the aspect, through method calls and T: A bounds, for anonymous and nominal records) and on a function whose parameter has an open-row tail ({ x, ..R }, a struct-residual tail, and the Rest of a where R = { .., ..Rest } decomposition), where every field of the argument beyond the ones the parameter names must satisfy each aspect, else T0012 names the field.

fun consume<row R>(r: { id: i64, ..R }) -> i64 where all R: Copy { r.id }
consume({ id = 1, label = "x".to_string() }); // T0012: field `label` ... does not implement `Copy`

The completed implementation also carries this entitlement into generic bodies. Since RFC-0179 a by-value open-row parameter owns its whole argument, so a body over an unconstrained <row R> may pass such a row on with no bound (spec.types.generics.open-rows.legality-3); where all R: Copy is only needed to use the row more than once. The entitlement applies to the row's fields, not to R as a whole. Other impl targets and a row generic not used as a parameter tail remain rejected with T0012, rather than silently ignoring the constraint.

Impact​

At the time, generic functions could narrow an unknown row and only fail when a caller instantiated the body with a non-Copy field. The completed implementation checks the generic body against its declared field-wise entitlement instead.

Affects​

Resolution​

Resolved by metel-core#1302 with RFC-0173's completed generic-body checking (metel-core#1364). Fixtures cover both the rejected unconstrained body and the accepted where all R: Copy form.