checker: the power-of-two rule for an inferred Flags #48
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
The rule reaches every written
Flags<Case>but not one whose type is only inferred (Flags.of(Permission.All)).Source
Acceptance criteria
FlagstypesDependencies
None known.
Moved from the planning lists of the docs; the design record stays the specification.
[loop:torbscript] intake: in scope (author orion, label loop). FLAGS.md section 6 names the gap; the rule itself is specified, so this is a checker change with a test and likely no owner question. Touches the checker (compiler/src), so an opus/high implementer; the seed is not expected to change. next: refine (ready buffer is 6, so not before it drops below 2).
[loop:torbscript] claim refine by session:loop-torbscript-20261008 until 2026-10-08T13:24:50Z
[loop:torbscript] refine: ready.
Where the rule lives.
requireFlagBits(compiler/src/semantics/checker/case-value.trb) is called from exactly one place,typeFromReference(compiler/src/semantics/checker/lowering.trb), i.e. only for a type the source writes (Flags<Permission>in an annotation, parameter, field or type argument). A type that is only inferred, such as the result ofFlags.of(Permission.All),Flags.from(list)orFlags.fromBits(...), is built incheckCall(compiler/src/semantics/checker/call.trb,solved = resolveTypes ...and the returnedresolveType checker, inner.result) and never passes throughtypeFromReference, soCaseis solved toPermissionwithout any look at its case values.Design record.
docs/design/FLAGS.mdsection 2 ("Every value must be a power of two for a type used asFlags<Case>") is the rule; section 6 names this gap. The rule and its message are already specified, only its reach is missing, so there is no open language choice.Goal. The existing diagnostic also fires when the type
Flags<Case>is only inferred.Acceptance criteria (tests in
compiler/tests/case-values.test.trb, groupFlags<Case>, using the existingcollectionsstub andproblemsOf):type Permission { case Read = 1 case Write = 2 case Both = 3 }andconst access = Flags.of(Permission.Read)(no annotation) reports exactly one problem:"`Permission.Both` stands for 3, and `Flags<Permission>` needs a power of two for every case", at the call expression. The note is the existing one.Flags.from([Permission.Read])orFlags.fromBits(1)bound to aconstwithout annotation) reports the same message once.const access: Flags<Permission> = Flags.of(Permission.Read)still yields exactly one problem (the written type and the inferred call result must not both report).Read = 1,Write = 2,Delete = 64) used throughFlags.of(Permission.Read)yields no problem, and so does a generic use whereCaseis a type parameter.torb check .still reports "no problems" (nothing instd/orcompiler/infers aFlagsof a non-power-of-two type).Implementation sketch. Run the same check on the resolved result type of a call in
checkCall(reusingrequireFlagBits, which already returns early for anything that is not a nominalFlags<T>; it needs the current module,checker.report modulevschecker.reportHere), and make sure a written type does not report a second time at the call. Free to pick the cleanest hook as long as 1-5 hold.Docs in the same PR (
docs:commit or part of the same commit):docs/design/FLAGS.md(section 2 bullet "whereFlags<Case>is written (requireFlagBits)" and remove the section 6 bullet; mark the gap as built with today's date),docs/language/types/case-values.mdline 87,docs/standard-library/collections.mdline 220 and the doc comment instd/collections/src/flags.trb("whereverFlags<Case>is written" becomes "written or inferred"), thentorb docs skill docs skillsto regenerateskills/.Size. One PR, two Conventional Commits:
fix(checker): hold an inferred Flags to the power-of-two rule(compiler/src + test) anddocs(flags): the rule reaches an inferred Flags(docs + std doc comment + regenerated skills).Seed: no (no syntax change, no new native, no driver flag, no new std name looked up by string;
wellKnown.flagsis already in use). Not a breaking change for existing code per criterion 5.Implementer model: opus/high (
compiler/src). Gates run in forge CI only (sh tools/gates.sh a).next: implement
[loop:torbscript] claim implement by session:loop-torbscript-20261008 until 2026-10-08T17:28:22Z