mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-05-03 10:14:52 +00:00
db6d29c0bd
Technical approaches covering: - Grammar modifications for mandatory annotations - AST-level type checking pass - Hindley-Milner style type inference - Subtype relations and union types - Null safety implementation - Helpful error messages with hints - Bytecode optimizations using type info - Gradual typing mode for migration - IDE/LSP integration points - Testing strategy for type system Includes priority order and list of CPython files to modify. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.5 KiB
8.5 KiB
Typthon Implementation Ideas
Technical approaches for implementing strict typing in Typthon.
1. Grammar Modifications (Grammar/python.gram)
Enforce Annotations at Parse Time
# Current (optional annotations)
function_def: 'def' NAME '(' params? ')' ['->' expression] ':' block
# Strict (required annotations for non-lambdas)
function_def: 'def' NAME '(' typed_params ')' '->' expression ':' block
typed_params: typed_param (',' typed_param)* [',']
typed_param: NAME ':' expression ['=' expression]
Pros: Errors at parse time, no runtime overhead Cons: Breaks all existing Python code immediately
Alternative: Compilation Flag
typthon --strict-annotations script.ty # Enforce at parse time
typthon --gradual script.ty # Allow missing annotations
2. AST-Level Type Checking
New Compiler Pass
Insert a type-checking pass between AST creation and bytecode generation:
Source → Lexer → Parser → AST → [TYPE CHECKER] → Optimizer → Bytecode
↑
New pass here
Location: Python/compile.c or new Python/typecheck.c
Type Representation
// Include/cpython/typeobject.h
typedef struct {
PyObject_HEAD
PyObject *type_name; // "int", "list[str]", etc.
PyObject *type_args; // Generic args (for list[T], dict[K,V])
int is_optional; // None allowed?
int is_union; // Union type?
PyObject *union_members; // Members if union
} TyTypeInfo;
Type Context
// Track types during compilation
typedef struct {
PyObject *locals; // name -> TyTypeInfo
PyObject *globals; // name -> TyTypeInfo
PyObject *returns; // expected return type
int in_function; // inside function body?
} TyTypeContext;
3. Type Inference Engine
Hindley-Milner Style Inference
For local variables, infer types from usage:
def process(items: list[int]) -> int:
total = 0 # Inferred: int (from literal)
for x in items: # Inferred: int (from list[int])
total += x # Confirmed: int += int
return total # Checked: int matches return type
Inference Rules
Literal Rules:
42 → int
3.14 → float
"hello" → str
True → bool
[1, 2] → list[int]
{"a": 1} → dict[str, int]
Assignment Rules:
x = expr → x : typeof(expr)
x: T = e → check typeof(e) <: T
Call Rules:
f(args) → return_type(f) where args match param_types(f)
Iteration Rules:
for x in iterable[T] → x : T
4. Type Compatibility Checking
Subtype Relations
// Is `sub` a subtype of `super`?
int TyType_IsSubtype(TyTypeInfo *sub, TyTypeInfo *super) {
// Same type
if (TyType_Equal(sub, super)) return 1;
// None is subtype of Optional[T]
if (TyType_IsNone(sub) && super->is_optional) return 1;
// T is subtype of Optional[T]
if (super->is_optional && TyType_IsSubtype(sub, super->base_type)) return 1;
// Covariant generics (list[Cat] <: list[Animal] if Cat <: Animal)
// ... careful here, depends on mutability
// Structural subtyping for Protocols
if (TyType_IsProtocol(super)) {
return TyType_ImplementsProtocol(sub, super);
}
// Class inheritance
return TyType_InheritsFrom(sub, super);
}
Union Types
def handle(x: int | str) -> None:
if isinstance(x, int):
# x narrowed to int here
print(x + 1)
else:
# x narrowed to str here
print(x.upper())
5. Null Safety Implementation
No Implicit None
# Python allows this implicitly
def bad() -> str:
pass # Returns None, but declared str!
# Typthon rejects this
# Error: Function may return None but return type is 'str'
Optional Type Handling
// In type checker
if (function_can_return_none(func) && !return_type->is_optional) {
TyErr_TypeMismatch(
"Function may return None but return type '%s' is not Optional",
return_type->type_name
);
}
None Narrowing
x: str | None = get_optional()
# Before check: x is str | None
if x is not None:
# After check: x is narrowed to str
print(x.upper()) # OK
# Outside check: x is still str | None
print(x.upper()) # Error: x might be None
6. Error Messages
Helpful Diagnostics
Error in foo.ty:15:10
14 | def process(items: list[str]) -> int:
15 | return items[0]
^^^^^^^^
TypeError: Cannot return 'str' from function declared to return 'int'
Hint: Did you mean to use 'len(items)' or convert with 'int(items[0])'?
Type Diff for Complex Types
Error in bar.ty:42:5
TypeError: Argument type mismatch for 'process_data'
Expected: dict[str, list[tuple[int, float]]]
Got: dict[str, list[tuple[int, int]]]
^^^^^
float ≠ int
Hint: The second element of tuples should be 'float', not 'int'
7. Bytecode Optimizations
Type-Specialized Instructions
// Current: Generic add
BINARY_ADD // Works for int, float, str, list...
// With types: Specialized versions
BINARY_ADD_INT // int + int, no type dispatch
BINARY_ADD_FLOAT // float + float
BINARY_ADD_STR // str + str (concat)
Skip Runtime Checks
def add(x: int, y: int) -> int:
return x + y # Compiler knows both are int
# Generated bytecode can skip:
# - Type checking x and y
# - Dynamic dispatch for __add__
# - Result type determination
8. Gradual Typing Mode
Per-Module Strictness
# strict.ty
from __future__ import strict_typing # Enable strict mode
def add(x: int, y: int) -> int: # Required
return x + y
# gradual.ty
# No strict_typing import - allows missing annotations
def add(x, y): # OK in gradual mode
return x + y
Boundary Checking
# When calling untyped code from typed code
from untyped_lib import mystery_func # No stubs
result: int = mystery_func(42)
# Runtime check inserted: assert isinstance(result, int)
9. Integration Points
IDE Support (LSP)
Expose type information via Language Server Protocol:
- Hover shows inferred/declared types
- Go to type definition
- Find all usages of type
- Refactoring with type awareness
Type Stub Generation
typthon --generate-stubs mymodule.ty > mymodule.tyi
C Extension Typing
// In extension module
static PyObject *
fast_add(PyObject *self, PyObject *args) {
// Declare types for Typthon
TY_SIGNATURE("(int, int) -> int");
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b))
return NULL;
return PyLong_FromLong(a + b);
}
10. Testing Strategy
Type System Tests
# tests/typing/test_inference.ty
def test_literal_inference():
x = 42
assert_type(x, int)
y = [1, 2, 3]
assert_type(y, list[int])
def test_narrowing():
x: int | str = get_value()
if isinstance(x, int):
assert_type(x, int) # Narrowed
else:
assert_type(x, str) # Narrowed
Error Message Tests
# tests/typing/test_errors.ty
def test_return_mismatch():
with expect_error("TypeError: Cannot return 'str'"):
def bad() -> int:
return "oops"
Priority Order
- AST Type Analyzer - Foundation for everything else
- Basic Function Checking - Enforce param/return types
- Type Inference for Locals - Reduce annotation burden
- Null Safety - Major safety win
- Union Types + Narrowing - Practical necessity
- Generics - list[T], dict[K,V]
- Protocols - Structural subtyping
- Optimizations - Performance payoff
Related Files to Modify
Grammar/python.gram- Syntax rulesParser/parser.c- Generated parserPython/ast.c- AST constructionPython/compile.c- Compilation (add type pass)Python/symtable.c- Symbol table (add type info)Include/cpython/typeobject.h- Type representationLib/typing.py- Standard library types