Row Bounds
Extracted from RFC-0090 on 2026-07-24 (superseded; see RFC-0116's header for the split rationale).
Depends on RFC-0116 (Anonymous Record Types) for the row syntax it reuses in bound position, and on nothing else. It does not depend on RFC-0121 (Open Rows): a bound is a predicate over a type, not a row variable, and the two were conflated in RFC-0090 partly because they shared a spelling.
Status — under review (2026-07-24). Scheduled for v0.12.0 alongside RFC-0116, which is its only dependency.
Status — accepted (2026-07-24). OQ1/OQ2 resolved by prototype (wildcard_type + row_bound built, 755 tests green, reverted). OQ3 resolved: bounds match the public projection of a row, making satisfaction module-relative -- acceptable because a bound grants no capability. OQ4 deferred to RFC-0121, which is not in this release and is the only thing that could make it reachable. OQ5 is an implementation-location question, not a design one.
Status — integrated 2026-07-24, targeting v0.12.0. A "Row bounds" subsection is merged into
public/reference/spec/types.mdunder Generics, covering open and closed bounds, the trailing.., negation with the type wildcard, why implicit structural satisfaction is safe, and the public-projection rule. Three one-line availability markers.Cross-checked against the siblings still in flight for v0.12.0, per
PROCESS.md:
- RFC-0116 (Anonymous Record Types) — this RFC's only dependency, now
3-integrated. No conflict: a row type and a row bound are distinguished by position, and by the..marker for the open reading, which RFC-0116's closed types never carry.- RFC-0115 (Field Initializer Separator) — no interaction. Bounds and row fields both classify and keep
:; RFC-0115 moves only initializers to=.- RFC-0071 (Ownership) — no interaction. Bound satisfaction is a static question about a type's fields, not about any value's multiplicity.
- RFC-0121 (Open Rows), not in this release — inherits two questions from here (impl-selection coherence, and the module-relative consequence of the public-projection rule). Both are unreachable in v0.12.0 because nothing in it lets an impl be keyed on a row; re-checked against RFC-0116 and this RFC rather than assumed.
The design questions that were open at review are settled, two of them by building the change rather than reasoning about it.
wildcard_typeand arow_boundalternative tobound_headwere added to the grammar, run against the suite (755 green), and reverted:T: { x: f64, y: f64, .. }andT: !{ token: … }both parse,T: Show + Cloneis unaffected. The prototype also settled a detail the RFC had not specified — the trailing..is admitted only after at least one field, so bare{ .. }does not parse.Amended 2026-07-24, hours after integration, and the amendment is the substantive part. As first integrated, §3 said any struct with matching fields satisfies a row bound implicitly, and open question 3 was resolved with a "public projection" rule to stop that leaking private fields. Both were wrong, and the second only existed because of the first.
What was missed: RFC-0090 contradicts itself on whether structs satisfy bounds. Its §2 and §7 say yes, implicitly; its §8 tier 1 and tier 2 say never. §7 even claims to be "resolved by the tier system (§8)" while asserting the opposite of §8. This RFC inherited §2/§7's side without noticing §8's, and the integration cross-check above did not catch it — it compared this RFC against its siblings and against the grammar, but not against the superseded parent's own tier text, which is where the conflict lived.
§3 now takes §8's side: a row bound is satisfied by a record, not by a struct. A struct converts first (RFC-0119). The tier rule then applies without exception.
Two consequences. The public-projection rule is withdrawn — a record has no declaring module and no private fields, so nothing needs projecting; the question moves to RFC-0119, where
to_record()is the actual capability. And in v0.12.0 row bounds are useful over record literals but not over structs, since conversions are not in this release — the headline case is deferred, not abandoned, and needs no further change to this RFC when RFC-0119 lands.This is
3-integrateddoing its job rather than failing at it, perPROCESS.md: a problem surfaced at integration sends the RFC back for amendment instead of carrying it into implementation. It surfaced late and by challenge rather than by my own cross-check, which is worth recording as the more useful fact.
Status — integrated (2026-07-24). Row bounds merged into public/reference/spec/types.md under Generics; three availability markers. Cross-checked against RFC-0116/0115/0071 and RFC-0121 (which inherits the impl-coherence and module-relative questions). Grammar verified by prototype.
Amended 2026-07-25, while assessing implementation readiness (#577). Three changes, all to §1/§2 and the grammar delta; the semantics of what satisfies a bound (§3) are untouched.
- The
recordkind marker is retained, after being proposed for deferral and the proposal withdrawn. The argument for dropping it — that a row bound already implies record-kindedness — fails on<record T>with no row bound, which is the only way to write "any record, whatever its shape" and is the signature RFC-0092's comptime row reflection needs. The kind gates what the body may do, not merely what the caller may pass. See §1.- Two forms are now specified that never were: the row bound is optional (
<record T>alone is valid), andrecordmay be written in awhereconstraint as well as at the parameter.- The type-position wildcard
_is withdrawn (open question 1, reopened and closed the other way). Its 2026-07-24 resolution addedwildcard_typetotype_expr, which would have made_writable in every type position while this RFC defined it in one. Replaced by letting a row field omit its type:{ x }means "a labelx, any type", in either polarity. §2 also now states that negation is the complement of the positive bound — consistent with!Copy— so!{ x: f64 }is satisfied by a record whosexis ani64, and!{ x }is the "no such label at all" form.The last two came out of challenges during the readiness review rather than from my own cross-check, which is the more useful fact to record.
Status — implemented (2026-07-25). Row bounds shipped in v0.12.0 (#577, merged 24b858e). Open/closed bounds, the trailing .., label-only fields, negative bounds as per-field absence, the record kind marker at the parameter or in a where clause, and the bound-less
form. The type-position wildcard was withdrawn during implementation. Useful over record literals only until RFC-0119 lands conversions; the RFC records that.
Status note, 2026-08-25 — Open Question 4 resolved without a shipped-behavior change. The brand-vs-row coherence question OQ4 deferred to RFC-0121 above is now answered there (RFC-0121 §3,
1-under-review): the brand-keyed impl wins over a row-conditional one. This RFC's own implemented behavior is unaffected — a bound alone still selects nothing — so no code or spec change follows here; see Open Questions below for the resolution text.
Summary
A bound written as a bare row: record T: { x: f64, y: f64, .. } means "any type carrying at
least these fields." A field may omit its type ({ x }) to constrain the label only.
Negation reuses the bound grammar's existing ! and is the complement of the positive
bound: T: !{ token } means "any type carrying no field named token." Any nominal struct with matching
fields satisfies a row bound with no explicit opt-in — this is the one implicit,
structural satisfaction rule in an otherwise nominal aspect system, and §3 explains why
that is safe here specifically.
Replaces the HasField<"x", f64> / Lacks<"tag"> aspect family, which never parsed —
bound_arg accepts only assoc_binding or type_expr, and type_expr has no
string-literal alternative.
Motivation
Generic code often wants "anything with an x and a y," not a specific nominal type.
Without a structural bound, every such case needs either a bespoke aspect per field shape
— unworkable at scale — or forces callers to wrap values in a common nominal type to
satisfy a bound that was never about identity.
GHC's HasField "x" r Float answers the same problem. The first draft of this feature
copied that shape directly and inherited a spelling Metel's grammar cannot parse; writing
the bound as a row instead removes the string literal, compacts an ANDed chain of
per-field facts into one bound naming several labels, and reuses syntax that already
exists for an unrelated reason.
1. Positive bounds, and the .. that makes them open
fun magnitude<record T: { x: f64, y: f64, .. }>(p: T) -> f64 { ... }
The record kind marker is required; a bare <T: { … }> is an error. (Adopted
2026-07-24.) A row bound is satisfiable only by a record (§3), so a type parameter carrying
one is record-kinded whether or not it says so. Making it say so follows the same
explicit-over-inferred principle as RFC-0065's "elision is never a silent choice" and as
<row R> itself, which is declared rather than inferred from ..R usage.
It is deliberately not spelled row T. row R (RFC-0121) declares R to be a row —
a label-to-type mapping, spliced as ..R. record T declares T to be a record type,
used directly as T. Those are different kinds, and reusing one keyword for both is the
error this cluster has spent its whole history removing.
The marker is capability-granting, not decorative
(Recorded 2026-07-25, after the marker was proposed for deferral and the proposal was
withdrawn.) The case for dropping it ran: a row bound is satisfiable only by a record, so
the kind is inferable from the bound and the marker adds nothing but explicitness. That is
wrong, and the reason is <record T> with no row bound at all.
fun labels<record T>(x: T) -> Symbol[] { ... } // any record, whatever its shape
There is no other spelling for that. <T> is unconstrained and admits i64; <T: { .. }>
does not parse, deliberately — see open question 2, where the trailing .. is admitted
only after at least one field. So without the marker, record-kindedness is obtainable only
as a side effect of constraining specific fields, and a function that wants "any record"
would have to invent a fake field constraint to get it.
That case is not hypothetical. RFC-0092 (Comptime Core) already models TypeInfo::Struct { row: Row } and states that the payload "is exactly what RFC-0090's row concept already
models", with typeinfo inspecting the row at comptime. Label-level operations over an
otherwise unconstrained record are a designed direction, and <record T> is the signature
they need.
So the kind gates what the body may do, not merely what the caller may pass. A row bound is an optional further constraint on an already-record-kinded parameter:
fun labels<record T>(x: T) -> Symbol[] // any record
fun magnitude<record T: { x: f64, y: f64, .. }>(p: T) // a record with at least x and y
Where the marker may be written
record may appear at the parameter's declaration or in a where constraint for it, the
same way an aspect bound may be written in either place. The two are equivalent, and writing
it in both is redundant but legal:
fun f<record T: { x: f64, .. }>(p: T) -> f64 { ... }
fun g<record T>(p: T) -> f64 where T: { x: f64, .. } { ... }
fun h<T>(p: T) -> f64 where record T: { x: f64, .. } { ... } // kind declared in `where`
A parameter is record-kinded iff record precedes its name in at least one of those two
positions. A row bound written for a parameter that is record-kinded in neither is the
error this section opened with — the diagnostic should name the fix (add record before the type parameter) rather than just rejecting the bound.
The marker composes with aspect bounds, since a named record may implement aspects:
fun render<record T: Show + { x: f64, .. }>(p: T) -> String { ... }
record is currently a valid identifier — let record = 5; compiles today (verified
2026-07-25) — so reserving it is a deliberate breaking change. It is cheap here (the
corpus uses the word only in comments) and unavoidable regardless: RFC-0120 needs the same
reservation for record X { … }, so taking it now costs one break rather than two.
The reservation is wider than that one example, as reservations always are. Once record
is a keyword it is unusable as any identifier — a let binding, a function name, a struct
field name, a record field label. Measured 2026-07-25 against the implementation: this
matches struct and extend exactly, so it is the language's normal keyword behaviour and
not a new class of breakage. (Not every keyword-ish word is reserved — aspect, for
instance, is still usable as a struct field name.) Worth stating precisely, because
"let record = 5 stops compiling" understates it: a domain model with a record: field also
stops compiling.
record is free as a keyword: checked against grammar.pest (not currently reserved) and
against stdlib/ and the test corpus (one occurrence, in a comment). No rename is needed,
unlike RFC-0098's var/std::env::var collision.
The trailing .. is load-bearing. It is an anonymous row variable — "and a rest I am
not naming" — and its presence is what makes the bound open:
fun f(p: { x: f64 }) // a closed type: exactly x
fun g<record T: { x: f64 }>(p: T) // a closed bound: T's row is exactly x
fun h<record T: { x: f64, .. }>(p: T) // an open bound: T has at least x
Without the marker, the closed and open readings would be spelled identically and told
apart only by grammatical position — and the closed bound reading could not be written at
all. The named form ..R (RFC-0121) is the same mechanism with the rest given a name.
2. Negative bounds
! already means "does not satisfy" everywhere else in the bound grammar — T: !Copy is
"T does not implement Copy". A negative row bound is the same idea applied to each named
field. Reuses bound = { bang? ~ bound_head } unchanged.
The precise rule: !{ … } holds when the record has no field matching any of the listed
entries. An entry matches when the label matches and, if a type is written, the field's
type is that type.
record T: !{ x: f64 } // holds unless T has an `x` of type f64
record T: !{ x } // holds unless T has an `x` at all, of any type
record T: !{ a, b: i64 } // holds when T has no `a`, and no i64-typed `b`
So !{ x: f64 } is satisfied by a record whose x is an i64 — it has no f64-typed
x. That is the surprising case and it is deliberate; !{ x } is the form that rejects the
label outright.
The complement is taken per listed field, not against the row as a whole. Reading it the
other way — "T does not satisfy the closed bound { x: f64 }" — would make !{ x: f64 }
hold for { x: f64, y: i64 }, since a two-field record does not satisfy a closed one-field
bound. That is plainly not the intent, and the distinction is invisible until someone writes
a wider record, so it is fixed here rather than left to the implementation.
Negative bounds take no ..: absence has no rest to quantify over. !{ x, .. } is a
parse error, not a no-op — the checker would otherwise ignore the .. silently, and
syntax that is accepted but meaningless tends to acquire a meaning later by accident.
The empty bound { } is legal and means "a record with no fields at all" — the closed
reading of an empty label set, satisfied only by { }. It falls out of the grammar rather
than being designed, and it is harmless and consistent (closed bounds require an exact label
set; the empty set is a set), so it is admitted rather than special-cased. Note this is not
the same as the rejected { .. }, which would mean "any row at all" — see open question 2.
2a. A field may omit its type, in either polarity
{ x } in bound position means "carries a label x, of any type". (Adopted 2026-07-25,
replacing a type-position wildcard — see open question 1.)
fun f<record T: { x, .. }>(p: T) // has an `x`; its type is unconstrained
fun g<record T: { x, y: f64, .. }>(p: T) // mixed: any-typed `x`, f64 `y`
fun h<record T: !{ token }>(t: T) // carries no `token`, whatever its type
Earlier drafts spelled the any-type case { x: _ }, with _ a new type-position wildcard.
Omitting the annotation says the same thing with no new construct, and it matches the shape
the language already uses when a part is inferable — a record literal's { x } likewise
drops what need not be written. The wildcard is therefore not introduced.
No ambiguity arises: a record type requires ident : type for every field
(record_type_field), so a bare { x } is not a well-formed type and this spelling is
reachable only in bound position.
3. What satisfies a row bound: records, not structs
A row bound is satisfied by a record. A nominal struct does not satisfy one, however
its fields are shaped. To pass a struct where a row bound is expected, convert it first —
h.to_record() (RFC-0119) — which is an explicit, opt-in capability the struct's author
granted.
fun magnitude<record T: { x: f64, y: f64, .. }>(p: T) -> f64 { … }
magnitude({ x = 3.0, y = 4.0 }); // a record — satisfies the bound
magnitude(some_point); // a struct — does not, whatever its fields
magnitude(some_point.to_record()); // explicit conversion, once RFC-0119 lands
This is the tier rule applied consistently, not an exception carved for bounds. No
structural capability is ambient: a plain struct (tier 1) has no row-shaped behaviour at
all, and gaining any of it — conversion, bound satisfaction, row-conditional impls — is
something an author opts into.
Why the alternative was rejected, since the corpus argued for it
An earlier draft of this section said the opposite: that any struct with matching fields satisfies a bound implicitly, on the argument that a bound grants no capability over the type itself — it only lets a generic function accept the type. That argument is correct as far as it goes, and it is why RFC-0090 §2 and §7 both stated the implicit rule.
An earlier version of this section rebutted it badly and the rebuttal is withdrawn. It
claimed implicit satisfaction creates "ambient structural compatibility — the TypeScript
collapse." That conflates two different things. TypeScript's collapse is subtyping: a
ScreenPos is a Point, substitutable wherever one is expected. A row bound does
nothing of the kind — under record T: { x: f64, .. }, Point and ScreenPos both satisfy
the bound and remain completely unrelated to each other. The function is polymorphic;
the types do not collapse. Constrained genericity is not substitutability.
The two arguments that do hold:
-
Privacy. Under implicit struct satisfaction,
{ secret: i64, .. }becomes an oracle for private structure: outside code can learn which fields a struct has by observing which bounds it satisfies. That defeats RFC-0032, and it is what forced the awkward "public projection" rule this RFC briefly carried and then withdrew. Records-only makes the problem vanish rather than needing a rule. -
Declaration-gating symmetry. Both bound kinds are opted into; they differ only in granularity. An aspect bound is opted into per aspect, by writing an impl. A row bound is opted into per type, by choosing the
recordkind. Nothing is ambient in either direction, and that is a sharper statement of the tier principle than "no capability is ambient." -
And the reason the opt-in is worth having, which the corpus never stated. RFC-0090 §6 justified the tier split on implementation cost — "the 99% of code that never writes a row bound pays for machinery it never asked for." That answers the wrong question. The real cost is borne by the type's author, not the compiler:
A nominal type's API is what it declares. A record's API is what it contains.
Once a type satisfies row bounds, its field names and types are public interface whether the author meant that or not. Renaming a field breaks every caller who wrote a bound mentioning it; adding one can make the type accidentally satisfy a bound its author never heard of. On a
struct, a field rename is internal. That is the disadvantage of purely structural type systems, and making it opt-in is the whole point of the distinction — not saving the typechecker work.This also retires an observation that looked like a design flaw. A named record can do everything a struct can plus satisfy row bounds, which reads as strict domination and invites "why would anyone write
struct?". That treats every capability as desirable. Publishing your layout is a capability most types should decline. The relation is a trade, not a hierarchy:encapsulation structural flexibility structlayout private; API is what you declare none record Xlayout is the API full Prior art agrees: Go deliberately refuses to let interfaces constrain on fields — methods only, so layout is never API. Rust is nominal with traits as the sole API surface. OCaml has both and uses objects sparingly. TypeScript is the counter-example that demonstrates the cost.
Why GHC's counter-example does not bind. GHC solves HasField directly against nominal
records with no conversion — the design rejected here. But GHC has only one kind of type,
so a structural predicate must apply to nominal types or be useless. Metel has a dedicated
kind for row behaviour, so it does not need to make structs structural: you write record
when you want it. PureScript is the closer analogue — it has both nominal data and
structural Record, and row machinery applies only to the latter, reached by unwrapping the
constructor. It sides with this RFC.
RFC-0090 contradicted itself on exactly this point, and the contradiction was inherited here before being caught during this RFC's own integration review:
- §2 — "any existing nominal struct with matching fields satisfies it with no explicit opt-in"
- §7 — "Resolved by the tier system (§8) — a struct satisfies a field-shape bound just by having the right fields, no opt-in required"
- §8 tier 1 — "no
Lacks/row-conditional typestate applicable to it" - §8 tier 2 — "no
HasField/Lacksbound is ever satisfied by it implicitly"
§7 claims to be resolved by §8 while asserting the opposite of what §8 says. This RFC takes §8's side. See the superseded RFC-0090 for the note recording the conflict.
Consequence for this release, stated plainly: in v0.12.0 a row bound is useful over record literals (RFC-0116) and not over structs, because RFC-0119's conversions are not in this release. The headline case — a generic accepting any struct with matching fields — is deferred, not abandoned; it arrives with RFC-0119, and needs no change to this RFC when it does.
4. Relationship to RFC-0116's closed types
A closed record type and a row bound are now spelled with the same braces, distinguished by
position: after : in a param or let annotation it is RFC-0116's exact type; after :
in a generic_param or where_constraint it is this RFC's predicate. They remain
semantically distinct — a closed type cannot be used as a predicate, a bound cannot be used
as a type — and with the .. marker present they are different token sequences, so no
position admits both readings.
Open Questions
-
The type-position wildcardClosed 2026-07-25 as not needed — the wildcard is withdrawn, not added. It was resolved on 2026-07-24 by prototyping_does not exist.wildcard_type = { "_" ~ !(ASCII_ALPHANUMERIC | "_") }intotype_expr, and that resolution is retracted.Why it was wrong: adding the alternative to
type_exprmakes_writable in every type position —let x: _ = 5,fun f(x: _),{ x: _ }as a record type — while this RFC defined its meaning in exactly one of them. It would have shipped a construct whose semantics were undefined almost everywhere it could be written, to be settled later by whoever first hit it.What replaces it: a row field may simply omit its type (§2a).
{ x }means "carries a labelx, of any type", in either polarity. Same expressiveness, no new construct, and it matches the shape the language already uses where a part is inferable. A general inference placeholder remains available as a separate future decision, on its own merits rather than as a side effect of negative bounds. -
Resolved 2026-07-24 by the same prototype, withbound_headneeds a new alternative.row_fieldsince amended to make the type optional (§2a):bound_head = { row_bound | type_path ~ ("<" ~ bound_arg ~ … ~ ">")? }row_bound = { "{" ~ (row_field ~ ("," ~ row_field)*)? ~ ("," ~ "..")? ~ ","? ~ "}" }row_field = { ident ~ (":" ~ type_expr)? }Both were built, run and reverted, rather than reasoned about:
- 755 tests green with both additions.
fun magnitude<T: { x: f64, y: f64, .. }>(p: T)parses — the failure ispath: unexpected rule row_field, a missing parser arm, not a grammar conflict.fun send<T: !{ token: _ }>(t: T)parsed, exercising the negative bound and the then-proposed wildcard together. The wildcard has since been withdrawn (open question 1); the negative-bound half of that result still stands, and the spelling is now!{ token }.fun f<T: Show + Clone>(t: T)still works — existing named bounds unaffected.
The prototype predates the
recordkind marker adopted later the same day (§1), so it exercised the bare<T: { … }>form. Three further grammar changes are therefore not yet verified, and they are the only unprototyped part of this RFC:generic_param = { record_kw? ~ ident ~ (":" ~ bound_list)? }where_constraint = { record_kw? ~ ident ~ ":" ~ bound_list }record_kw = @{ "record" ~ !(ASCII_ALPHANUMERIC | "_") }Note
generic_param's bound stays optional, which is what admits the bound-less<record T>form §1 relies on. Reservingrecordis a deliberate breaking change —let record = 5;compiles today — and is discussed in §1.One detail the prototype settled that the RFC had not specified: the rule above admits the trailing
..only after at least one field, so{ .. }alone does not parse. That is now load-bearing rather than incidental: it is precisely why therecordmarker cannot be dropped, since "any record, whatever its shape" has no other spelling than<record T>(§1). Written as a bound it would have to be{ .. }, which the grammar rejects. -
Cross-module private-field leakage.Withdrawn 2026-07-24 — the question does not arise for this RFC, and the resolution briefly recorded here is retracted. It was resolved as "a bound matches the public projection of a type's row," which made bound satisfaction module-relative and left a hole for negative bounds (!{ secret: _ }would have succeeded outside the declaring module and failed inside — an affirmatively wrong answer, not merely a non-match).Both the rule and its hole were artifacts of §3's earlier claim that structs satisfy bounds directly. Under §3 as it now stands, a row bound is satisfied by a record, and an anonymous record has no declaring module and no private fields — so there is nothing to project and nothing to leak.
The question is real and moves to RFC-0119, where it belongs: what does
to_record()produce for a struct with private fields, and who may call it? That is a question about a conversion, which is a capability, which is what the tier system actually governs. (From RFC-0090 OQ7, via RFC-0116 OQ3, now RFC-0119's.) -
Coherence between structural and nominal impl selection — real, but not reachable in v0.12.0, and therefore not blocking. An ordinaryResolved 2026-08-25 — RFC-0121 §3 (nowextend Point: Displayis keyed on nominal identity; RFC-0121's row-conditional impls are keyed on shape, and if a value matched both there would be no written rule for which wins. That collision needs impls keyed on rows, which is RFC-0121 and is not in this release — this RFC contributes bounds only, and a bound selects nothing. Re-checked rather than assumed: nothing in RFC-0116 or this RFC lets an impl be written against a row. Deferred to RFC-0121, where open question 3 above also lands. (From RFC-0090 OQ6.)1-under-review, the release this question was deferred to having arrived): the brand-keyed impl wins. Brand-exact dispatch is checked before row-conditional resolution is attempted, so a match there short-circuits it rather than conflicting with it under RFC-0060 §2. Recorded here for traceability; this RFC's own4-implementedstatus and shipped behavior are unaffected, since a bound alone still selects nothing. Owning implementation issue: metel-core#833. -
How does row-membership checking relate to RFC-0096's auto-impl algorithm? RFC-0096 §7 worked out that
HasField-style satisfaction is existential, not the universal recursionSend/Sync/Linearuse, so it does not fit that algorithm. The bare-row spelling does not change this — only the surface. What checks a row bound, and where it lives in the typechecker, is unspecified.
References
public/rfcs/5-superseded/rfc-0090-structural-records.md§1, §2, §7 — the source- RFC-0116 (Anonymous Record Types) — the row syntax reused in bound position
- RFC-0121 (Open Rows,
1-under-review) —..R, the named form of §1's anonymous..; §3 there resolves this RFC's own Open Question 4 (2026-08-25) - RFC-0096 (Auto-Impl Aspects) §7 — works out precisely how row-membership differs from
the
Send/Sync/Linearauto-impl algorithm, and flags the same coherence gap - RFC-0080 (Standard Library Aspects) — the auto-impl pattern this extends structurally
- RFC-0060 (Aspect Impl Coherence), RFC-0061 (Structural Aspect Bounds) — the coherence checking OQ4 would extend
- RFC-0032 (Field-Level Visibility) — the visibility model OQ3 must be reconciled with
reports/substructural-types/nominal-types-as-branded-rows.md§12 — the derivation of the bare-row bound spelling and whyHasFieldwas replaced outright
Decision
Outcome: (pending) Target: (set when accepted)