Type System Architecture
Relevant source files
The following files were used as context for generating this wiki page:
The Pop Lang type system is a strong, static, and native architecture designed to eliminate runtime dynamic fallback. Every runtime value and operation in Pop has a compiler-proven type, ensuring that inference never weakens static checking. The system is built to avoid the "Lua regression" of universal dynamic tables, instead providing distinct semantic representations for records, classes, interfaces, and collections.
Sources: README.md:14-22, architecture/README.md:12-18, AGENTS.md:175-180
Core Principles and Invariants
The type system is governed by several strict invariants that define the language's identity as a native Luau extension with static foundations.
- No Operational Dynamic Escape Hatch: Pop strictly forbids
Any,Dynamic, or unchecked runtime lookups. Every operation must have one statically known meaning across all backends. - Strong Inference without Fallback: While the system employs local type inference to reduce ceremony, this inference must always resolve to a concrete static type. If inference fails, it is treated as a compile-time error rather than a transition to runtime typing.
- Distinct Semantic Concepts: Unlike Lua, Pop does not collapse all structures into tables. Records, classes, unions, tuples, arrays, and tables are distinct at both the type level and the runtime representation level.
Sources: AGENTS.md:19-22, README.md:112-117, architecture/README.md:54-57
Type System Responsibilities
The type system component is centrally responsible for managing the lifecycle of types during compilation.
| Component | Responsibility |
|---|---|
| Type Arena | Ownership and storage of canonical semantic types. |
| Inference | Managing inference variables and constraint solving. |
| Subtyping | Determining relationships and compatibility between types. |
| Normalization | Simplifying and standardizing type representations. |
| Overload Resolution | Selecting non-generic functions by exact argument type packs. |
Sources: architecture/11-compiler-component-architecture.md:89-94, architecture/README.md:104-106
Architecture Hierarchy
The type system exists within a broader semantic pipeline. It interacts primarily with the Resolver to bind symbols and provides the necessary metadata for High-Level Intermediate Representation (HIR) construction.
The diagram shows the data flow from raw syntax through resolution into the type system, eventually powering IR generation.
Sources: architecture/11-compiler-component-architecture.md:5-18, architecture/11-compiler-component-architecture.md:89-94
Type Categories
Pop classifies types into several native categories, each with specific semantics and backend lowering strategies.
Primitive and Scalar Types
Pop replaces the universal "number" type found in Lua with explicit, fixed-width numeric types.
- Integers: Signed and unsigned variants (e.g.,
Int64,UInt32).Intis a standard alias forInt64. - Floating-Point:
Float32andFloat64(aliased asFloat). - Booleans: Native
Booleantype. - Never: A special type representing values that can never occur (e.g., the return type of a function that always panics).
Sources: README.md:214-223, LIST.md:18-20
Nominal and Data Types
The system prioritizes data-first structures over object-oriented hierarchies.
- Records: Typed data structures (e.g.,
public record Player). - Tagged Unions: Sum types used for explicit state handling (e.g.,
union LoadResult). - Enums: Nominal scalar enumerations.
- Interfaces: Nominal polymorphic boundaries used for stable identity and dispatch.
Sources: README.md:173-176, README.md:185-190, architecture/19-architecture-conformance-and-regression-policy.md:106-107
Collections
Collections in Pop are strictly typed. Heterogeneous collections are not permitted unless they use an explicit union or shared interface.
- Arrays: Written as
{T}, representing fixed-size or standard indexed sequences. - Typed Tables: Written as
{[K]: V}, representing associative maps with specific key/value types.
Sources: README.md:205-212, architecture/19-architecture-conformance-and-regression-policy.md:111-114
Resolution and Inference Logic
The type system resolves types through a deterministic query-based engine. It does not perform "speculative" type-checking of overloads that might destabilize diagnostics.
Overload Resolution
Overload resolution is strictly static. The compiler selects exactly one non-generic function based on the argument type pack.
- Participating Factors: Argument types and counts.
- Non-participating Factors: Result context, declaration order, and runtime values.
Sources: architecture/README.md:104-106, architecture/11-compiler-component-architecture.md:92-94
Generics and Specialization
Pop uses portable specialization capsules to preserve exact types across dependency boundaries.
- Inference: Generic calls infer a complete canonical argument list or fail.
- Monomorphization: The system supports full-specialization baselines but rejects erased or dynamic fallbacks.
Sources: architecture/README.md:100-103, architecture/19-architecture-conformance-and-regression-policy.md:119-121
Conformance and Safety
The type system architecture includes a "No Lua Regression" suite, which ensures that dynamic patterns cannot be reintroduced into the language.
This diagram illustrates how type violations and forbidden dynamic patterns are handled by the compiler's safety gates.
Sources: architecture/19-architecture-conformance-and-regression-policy.md:73-88, architecture/19-architecture-conformance-and-regression-policy.md:217-220
Implementation Details
The compiler implementation resides in focused Rust crates under crates/compiler/. The types crate owns the type arena and subtyping logic, while resolve handles symbol binding.
// Conceptual structure of type-related crates
compiler/
types/ // Type arena, constraints, inference
resolve/ // Symbols, scopes, binding
hir/ // Typed language IR
Sources: architecture/11-compiler-component-architecture.md:23-33
Diagnostic Integration
Type errors are emitted as structured APIs with stable POP#### codes. The system provides "quick fixes" for common type mismatches, though it never suggests Any or unsafe casts as a valid fix.
Sources: AGENTS.md:267-275, architecture/11-compiler-component-architecture.md:126-129
Conclusion
The Pop Lang Type System Architecture serves as the bedrock for the language's native performance and safety. By enforcing a strict boundary between static definitions and runtime execution, it ensures that every operation is predictable and optimized. Its rejection of dynamic fallbacks and "universal tables" distinguishes it from its Lua inspirations while maintaining a visually similar, low-ceremony syntax.