---
name: mps-aspect-typesystem
description: >-
  Use when authoring or debugging MPS typesystem — inference rules (`typeof :==: / :<=: / :>=:`), `SubtypingRule`, `ComparisonRule`, `InequationReplacementRule`, `SubstituteTypeRule`, `WhenConcreteStatement` blocks, `NonTypesystemRule` checking rules, `TypesystemQuickFix`, error/warning/info reports with `messageTarget` highlighting and quick-fix wiring (`helginsIntention`), and shared BaseLanguage helpers in the typesystem model. For questions to the compiled typesystem (`IS_SUBTYPE`, `EXPLAIN_TYPE`, `GET_EXPECTED_TYPE`), use the query tools when debugging “type stays undefined” or “operator cannot be applied.” Reach for this skill whenever the task involves editing `<lang>/languageModels/typesystem.mps`.
type: reference
---

# MPS Typesystem and Checking Aspect

## Loading companion skills

Companion names in this skill are lazy dependencies: load only those relevant to the current task. If this skill came from an MCP server, use the host's skill loader to resolve the companion's unique discovered entry URI on the same host-assigned originating server. If the host has no server-backed skill loader, stop and report that limitation; do not silently fall back to a filesystem copy. If this skill came from a filesystem catalog, load the named sibling from that same catalog at `<skills-root>/<skill-name>/SKILL.md`, even if remote skill loaders are also available. Do not invent a tool name or server endpoint.

The **typesystem** aspect gives nodes *types* and reports semantic errors. It combines two related sub-aspects:

- **Typesystem rules** — compute types and constraints on them (`InferenceRule`, `SubtypingRule`, `InequationReplacementRule`, `ComparisonRule`, `SubstituteTypeRule`).
- **Non-typesystem checking rules** — produce errors/warnings/messages without contributing to type inference (`NonTypesystemRule`, a.k.a. "checking rule").

Lives in `<lang>/languageModels/typesystem.mps`, language `jetbrains.mps.lang.typesystem`. Rule bodies are BaseLanguage + smodel + collections + closures.

## Critical Directives

- One `InferenceRule` (or other root rule) per concept whose type/check you compute. Multiple rules collectively constrain a node — keep each rule focused.
- The `infer` prefix makes an inequation *soft* (the solver tries to satisfy it, will not immediately error). Without `infer`, violating the inequation reports an error. Choose deliberately.
- In equation/inequation JSON, both `leftExpression` and `rightExpression` are `TypeClause` slots — **always wrap the real Expression in a `NormalTypeClause`** (`normalType` child holds the actual Expression). Do not put the Expression directly under `leftExpression`/`rightExpression`.
- TextGen-style dispatch caveat does **not** apply here; typesystem rules are inherited via concept hierarchy, but the `overrides` property on a rule suppresses inherited rules from superconcepts.
- TextGen / typesystem error messages: wrap smodel expressions that render types with `<...>` presentation: `error "Expected " + <expectedType> + " but got " + <actualType> -> node;`. Avoid raw `toString`.
- `when concrete (typeof(expr) as v) { ... }` defers a block until the type is fully resolved — use it before deciding whether to report an error or assign a result type.
- Quick fixes (`TypesystemQuickFix`) are roots, **not** executed automatically — the user triggers them via the UI. Wire them into a report through the `helginsIntention` slot (`TypesystemIntention` wrapper with `quickFix` ref + `actualArgument`s). See `references/quick-fixes.md`.
- Reusable helper code (utility classes, shared algorithms) can live as a plain BaseLanguage `ClassConcept` root **directly in the typesystem model**. No separate utility module is required.
- For MPS-typed return types (`sequence<node<X>>`, `list<node<X>>`) the Java parser gives back `List<SNode>` — fix per `references/variable-declarations.md` in the `mps-model-manipulation` skill root after loading that companion skill from the same origin.
- Edit typesystem models through MPS MCP tools (`mps_mcp_insert_root_node_from_json`, `mps_mcp_update_node`, `mps_mcp_parse_java_and_insert`). Do not hand-edit `.mps` files.
- After edits run `mps_mcp_check_root_node_problems`, compile the language, and test on sample models.

## Common-Path Workflow

0. **Ask the compiled typesystem before authoring a rule.** Check whether a type relation already holds or what produces a node's current type with `mps_mcp_query_types` (`IS_SUBTYPE`, `SUPERTYPES`, `OPERATION_TYPE`, `EXPLAIN_TYPE`) or `mps_mcp_query_nodes` (`GET_TYPE`, `GET_EXPECTED_TYPE`). Do not simulate the solver by reading rule roots: print a rule root only to change it or to pick a sibling to copy, and ask the engine (`OPERATION_TYPE`, `COMMON_SUPERTYPE`) what a change does to operators and joins. See `mps-mcp-workflow/references/analysis-tools/type-queries.md` for all operations and caveats.
1. Create a `typesystem` model (`mps_mcp_create_model` with `moduleName: "<lang>"` and `modelName: "<lang>.typesystem"` — aspect ID `typesystem`, case-sensitive, no `@` suffix; see [aspect-model-stereotypes.md](references/aspect-model-stereotypes.md)) if absent.
2. Add used languages: `jetbrains.mps.lang.typesystem`, and any languages used in bodies (`smodel`, `collections`, `closures`, `baseLanguage`).
3. Add `InferenceRule` roots for concepts whose types you compute (see `references/inference-rules.md`).
4. Add `SubtypingRule` / `ComparisonRule` / `InequationReplacementRule` / `SubstituteTypeRule` roots for the type lattice (see `references/lattice-rules.md`).
5. Add `NonTypesystemRule` roots for checks that are not about types (see `references/non-typesystem-checking.md`).
6. Add `TypesystemQuickFix` roots for any fix you want to attach to reports; wire them via a `helginsIntention`/`TypesystemIntention` child of the `warning`/`error`/`info` statement (see `references/quick-fixes.md`).
7. Write bodies with BaseLanguage + smodel. Use `when concrete` (`references/when-concrete.md`) when you need resolved types before checking. For helper code, place a `ClassConcept` root directly in the typesystem model and call it from rules.
8. Validate: `mps_mcp_check_root_node_problems` plus compile the language and test on sample models. A `helginsIntention`-wired quick fix now surfaces in the check report's `quickFixes` array and can be applied with `mps_mcp_apply_intention(nodeReference = <problem node>, intentionId = <fix id>)`; an auto-applicable fix runs under `autoApplyQuickFixes=true`.
9. Verify the rules against a sandbox you own with the matching engine query; do not infer their behavior from what `mps_mcp_check_root_node_problems` does or does not report:
   1. `mps_mcp_create_model` a scratch model in the language's sandbox solution; `mps_mcp_model_used_language` ADD (`kind: language`) the language under test and whatever the copied root's own concept needs (e.g. `jetbrains.mps.baseLanguage`).
   2. `mps_mcp_alter_nodes` `COPY_NODE` a sample root (the copy lands in the sample's model), then `MOVE_NODE_TO_PARENT` it with only `modelReference` = the scratch model (node ids survive the move; swap the model part of references you harvested before it); or `mps_mcp_insert_root_node_from_json`. Add the construct whose typing you will change.
   3. **Before** the change: `mps_mcp_check_root_node_problems` + `EXPLAIN_TYPE` on it, so you have the baseline error and the rule behind it (a problem's `rules[0].rule` names it; print `ruleReference` only to change that rule). Commented-out code is not checked (`uncheckedSubtrees`), so a clean verdict over it proves nothing.
   4. Author the rule — copy-then-modify a sibling rule (`COPY_NODE`, then `mps_mcp_update_node` the name and references) — then `MAKE` with `parameters` = `{"modules":["<lang>"],"rebuild":true}`. One MAKE answering `runtimeReady:true` is the reload; no `mps_mcp_reload_all`.
   5. **After**: ask again — `IS_SUBTYPE`, `mps_mcp_query_nodes` `GET_TYPE`, `EXPLAIN_TYPE` (the `subtype_checked` entry's `via` names the rule that made it fit), and `OPERATION_TYPE` / `COMMON_SUPERTYPE` for side effects of a lattice change.
   6. `mps_mcp_update_model` DELETE the scratch model when you no longer need it (keep it if the task asks for it). MPS's VCS integration may stage files the MAKE writes; an entry `git status` then shows as `AD` (staged, since deleted) needs `git rm --cached`.

## Related Skills

- `mps-aspect-behavior` — behavior methods called from rule bodies via `node.method()`; common host for `getType` / `isAssignableFrom`-style helpers callable from typesystem.
- `mps-aspect-constraints` — non-type validation often lives in constraints; consider whether a check belongs there before adding a `NonTypesystemRule`.
- `mps-aspect-intentions` — distinct from `TypesystemQuickFix` (intentions are user-invoked from caret; quick fixes attach to a report).
- `mps-collections-and-closures` — `.where` / `.select` / `.translate`, `foreach` and closure literals inside rule bodies.
- `mps-model-manipulation` — full BaseLanguage / smodel reference used inside rule bodies (`StatementList`, `DotExpression`, smodel operations, the `List<SNode>` → `sequence<node<X>>` return-type fix); for an inference or checking rule body open only `references/dot-expression-basics.md` in the `mps-model-manipulation` skill root after loading that companion skill from the same origin.
- `mps-quotations` — `<type>` literals in rule bodies are heavy quotations; `%(expr)%` splices use the `Antiquotation` family. The typesystem model usually uses `jetbrains.mps.lang.quotation` as a used language.
- `mps-aspect-structure-concepts` — when introducing the type concept(s) the rules target.

## Reference Index

**Start here — most common case**: one `InferenceRule` that types a concept → read only `references/inference-rules.md`, plus `references/json-blueprints.md` when inserting it through MCP; a checking rule that reports an error → only `references/non-typesystem-checking.md`; a type that stays `undefined` → only `references/common-failures.md`.

- Open `references/inference-rules.md` when writing or debugging an `InferenceRule` — the operator vocabulary (`:==:`, `:<=:`, `:>=:`, soft `infer`, strong `:<<=:` / `:>>=:`), free type variables (`var elementType;`), `join(A | B)`, `%(...)%` anti-quotations in `<...>` literals, and the four worked examples (StringLiteral, ParenthesizedExpression, TernaryOperator, ForEachStatement).
- Open `references/when-concrete.md` when a rule must wait for a resolved concrete type before deciding — `WhenConcreteStatement` shape, nested `when concrete` blocks, `operation type(op, leftType, rightType)`, and the bound-variable plumbing (`WhenConcreteVariableDeclaration` / `WhenConcreteVariableReference`).
- Open `references/lattice-rules.md` for the type-lattice rules — `SubtypingRule` (single supertype or `nlist<>` of supertypes), `InequationReplacementRule` (a.k.a. "replacement rule", structural subtyping with `isApplicableClause`, `isWeak`, `equationInfo.getNodeWithError()`), `ComparisonRule` (`boolean` body, `weak`), and `SubstituteTypeRule` (`attributedNode`, return-or-null pattern).
- Open `references/non-typesystem-checking.md` when adding a `NonTypesystemRule` ("checking rule") — `error / warning / info "msg" -> node;`, `isStrongSubtype(typeof(x) :<< t)` for strict-subtype tests, and the `CheckExcessTypeCasts` example.
- Open `references/quick-fixes.md` when authoring a `TypesystemQuickFix` and wiring it into a report — `QuickFixArgument` declarations, `executeBlock` / `descriptionBlock`, the three sample fixes (`RemoveExcessTypeCast`, `RemoveMisplacedDash`, `HideCardinalityOne`), and the full JSON for the `helginsIntention` / `TypesystemIntention` / `TypesystemIntentionArgument` plumbing.
- Open `references/messages-and-helpers.md` when crafting messages — the `<...>` type-rendering convention, `messageTarget` to highlight just a property/reference/child cell, `PropertyMessageTarget` shape, and shared BaseLanguage helper roots (`ElementSummary` pattern).
- Open `references/json-blueprints.md` when inserting nodes via MCP — body-level skeletons for each equation/inequation kind, `ReportErrorStatement` / `WarningStatement` / `InfoStatement` with optional `helginsIntention` / `messageTarget`, anti-quotation handling inside quotations, minimal rule-root skeletons, and the full validated concept reference with FQNs and concept refs.
- Open `references/common-failures.md` when a type is `undefined`, subtyping isn't applied, error messages render badly, return types fight the Java parser, rules cycle, quick fixes aren't offered, `when concrete` bodies never run, or `SubstituteTypeRule` seems ignored.
