Skip to content

Validators declare the types they apply to #97

Closed

Description

@Kinflou

@validators = [StringBounds(...)] on an s32 field builds without error today. The design note says applying a validator to a type it doesn't fit should be a build error at that field, but nothing checks it.

Proposal

A validator declares the type(s) it applies to:

validator StringBounds for string {
    ...
}
  • comline build rejects a validator applied to a different type, at the field (or alias, Validators on type aliases #99) that applies it: "StringBounds applies to string, not s32".
  • value.* paths are type-checked once, at the declaration, instead of separately at every use.
  • The editor can offer only the validators that fit a field's type.
  • Resolution: aliases resolve to their target first; an optional T field checks against T.
  • Arrays: a validator applies to exactly the declared type. StringBounds on a string[] field is an error, not "check each element". To check each element, put the validator on an alias of the element type and use Name[] (Validators on type aliases #99).
  • A list of targets (for string, T[]) can come later if a validator really fits several kinds of type.
  • A target written explicitly is preferred over inferring it from the accessors used. Inference would accept StringBounds on arrays too (both have a length), where its "characters" messages are wrong.

Decided (2026-10-18)

  • for is required. A validator without it is a build error (validation::validator-missing-target) with a fix-it hint. There's no deprecation period; the grammar keeps for optional only so the error can be specific.
  • Several targets as a list: validator Range for u8, u16, u32, s32, s64. No type categories (integer, array) and no generics for now. "Any array" is left to bounds on types (Bounds written on types: T[a..b], string[..n] #98).
  • Targets are primitives, arrays of them, and aliases (resolved to the type they stand for). Struct targets (checks across fields via value.<field>) come later.
  • Matching is exact after resolving aliases and stripping optional: u8 doesn't match u32.
  • Numbers compare by mathematical value in validate conditions (e.g. a u8 value against an s64 parameter). Generators emit lossless comparisons (widen or check the conversion), never a narrowing cast. Implemented with Run validators in generated code: TypeScript and cross-schema validators (phase 4d) #96.
  • Imported validators are checked too, not skipped as they are today (maybe_imported). std::validators::StringBounds is the main case. Resolution goes through a lookup the caller supplies (like size.rs's SizeLookup): over ProjectContext in the build, over the project in the language server.

value.length

Define it in core: for string, the number of Unicode scalar values; for arrays, the number of elements. Generators follow this definition (#96).

Tasks

  • Grammar: optional for TYPE on validator
  • Freeze the target on FrozenUnit::Validator
  • Check at every application site, and check value.* against the target
  • Error on a validator with no for; migrate std::validators::StringBounds to for string
  • Check imported validators through a caller-supplied lookup (build: ProjectContext; LSP: project)
  • Update guide/idl/validator.md and design/validators.md

Activity

  1. Kinflou commented on Oct 18, 2026

    @Kinflou
    MemberAuthor

    Done in core#105: validator X for T[, T]*, required. Mismatched fields, a missing for, and value accessors a target doesn't have are build errors. Imported validators and aliases are checked through ImportLookup: ProjectImports in the build, and the project lookup in the language server (language-server#61). Docs: docs#35. Numbers comparing by value is recorded in design/validators.md and gets implemented with #96.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions