diff --git a/src/check/canonicalize/CIR.zig b/src/check/canonicalize/CIR.zig index 615bce79be..f856c01243 100644 --- a/src/check/canonicalize/CIR.zig +++ b/src/check/canonicalize/CIR.zig @@ -329,133 +329,25 @@ pub fn diagnosticToReport(self: *CIR, diagnostic: Diagnostic, allocator: std.mem }; } -// ======================================================================== -// TYPE VARIABLE WORKFLOW DOCUMENTATION -// ======================================================================== -// -// The Roc type system uses a hierarchical approach to type variables during -// canonicalization. Understanding this workflow is crucial for working with -// the type checking system. -// -// ## TYPICAL WORKFLOW FOR EXPRESSIONS -// -// 1. **Reserve Node Slots**: Use `predictNodeIndex()` to reserve CIR node slots -// before creating type variables. This ensures consistent indexing. -// -// 2. **Create Type Hierarchy**: Build from most general to most specific: -// ```zig -// // For integer literal "42": -// const final_expr_idx = self.can_ir.store.predictNodeIndex(3); -// -// // 1. Flexible polymorphic variable (can be any numeric type) -// const poly_var = self.can_ir.pushFreshTypeVar(final_expr_idx, region); -// -// // 2. Integer-specific variable (references poly_var) -// const int_var = self.can_ir.pushTypeVar( -// Content{ .structure = .{ .num = .{ .int_poly = poly_var } } }, -// final_expr_idx, region -// ); -// -// // 3. Numeric variable (references int_var) -// const num_var = self.can_ir.env.types.freshFromContent( -// Content{ .structure = .{ .num = .{ .num_poly = int_var } } } -// ); -// ``` -// -// 3. **Create Expression**: Add the actual CIR expression node: -// ```zig -// const expr_idx = self.can_ir.store.addExpr(CIR.Expr{ -// .int = .{ -// .num_var = num_var, -// .requirements = requirements, -// .value = parsed_value, -// .region = region, -// }, -// }); -// ``` -// -// 4. **Associate Final Type**: Link the expression with its type: -// ```zig -// _ = self.can_ir.setTypeVarAtExpr(expr_idx, -// Content{ .structure = .{ .num = .{ .num_poly = int_var } } } -// ); -// ``` -// -// ## TYPE ANNOTATION WORKFLOW -// -// Type annotations follow a different pattern focused on creating constraints -// rather than inference placeholders: -// -// 1. **Extract Type Variables**: Find all type variables (like `a`, `b`) in the annotation -// 2. **Create Scope**: Enter new scope for type variables -// 3. **Introduce Type Variables**: Add them to scope as rigid variables -// 4. **Canonicalize Annotation**: Convert AST type annotation to CIR format -// 5. **Store for Connection**: Save annotation to connect with next definition -// -// ## COMMON PATTERNS -// -// **Polymorphic Types**: Use `pushFreshTypeVar` for the flexible part: -// - Integer literals before knowing if they're U64, I32, etc. -// - List elements before knowing the element type -// - Function parameters in generic contexts -// -// **Structured Types**: Use `pushTypeVar` with specific content: -// - Numeric types with hierarchy (num_poly -> int_poly -> flex_var) -// - Record types with field information -// - Function types with argument and return types -// -// **Error Recovery**: Use `pushMalformed` for compilation errors: -// - Invalid syntax that can't be parsed -// - Type errors that should be reported but don't stop compilation -// -// ======================================================================== - -/// Creates a fresh flexible type variable that can be unified during type inference. +/// Creates a fresh flexible type variable for type inference. /// -/// A flexible type variable (flex_var) represents an unknown type that the type -/// checker will attempt to infer. This is the most common type variable used -/// during canonicalization of expressions. +/// This is a convenience wrapper around `pushTypeVar(.{ .flex_var = null }, ...)`. +/// Use this for expressions where the type needs to be inferred, like integer +/// literals before knowing their specific type (U64, I32, etc.). /// -/// **Usage**: -/// - For expressions where the type needs to be inferred (e.g., integer literals before knowing if they're U64, I32, etc.) -/// - As polymorphic type variables in generic contexts -/// - When you need a "placeholder" type that will be resolved later -/// -/// **Example**: Integer literal `42` gets a fresh type var that will be unified -/// with the specific integer type based on context (U64, I32, etc.). -/// -/// **Relationship**: Often used as the "top level" in type hierarchies, with more -/// specific types created using `pushTypeVar` that reference this flexible var. pub fn pushFreshTypeVar(self: *CIR, parent_node_idx: Node.Idx, region: base.Region) types.Var { return self.pushTypeVar(.{ .flex_var = null }, parent_node_idx, region); } /// Creates a type variable with specific type content. /// -/// Unlike `pushFreshTypeVar`, this creates a type variable with predetermined -/// structure or constraints. The content parameter specifies what kind of type -/// this represents. +/// Use this to create type variables with predetermined structure or constraints, +/// unlike `pushFreshTypeVar` which creates unconstrained flexible variables. /// -/// **Usage**: -/// - Creating structured types (e.g., `Content{ .structure = .{ .num = .{ .int_poly = poly_var } } }`) -/// - Setting up type hierarchies where one type references another -/// - Creating rigid type variables with specific names -/// - Building complex types like records, functions, or tag unions -/// -/// **Example**: For integer literal `42`: -/// ```zig -/// const poly_var = self.can_ir.pushFreshTypeVar(final_expr_idx, region); -/// const int_var = self.can_ir.pushTypeVar( -/// Content{ .structure = .{ .num = .{ .int_poly = poly_var } } }, -/// final_expr_idx, region -/// ); -/// ``` -/// This creates an integer type that references the polymorphic variable. -/// -/// **Common Content Types**: -/// - `.flex_var` - Flexible type variable (same as pushFreshTypeVar) -/// - `.rigid_var` - Named type variable (e.g., in generic type signatures) -/// - `.structure` - Concrete types (nums, records, functions, etc.) +/// **Common content types**: +/// - `.flex_var` - Flexible (same as pushFreshTypeVar) +/// - `.rigid_var` - Named type variable for generics +/// - `.structure` - Concrete types (nums, records, functions) /// - `.err` - Error type for malformed code pub fn pushTypeVar(self: *CIR, content: types.Content, parent_node_idx: Node.Idx, region: base.Region) types.Var { // insert a placeholder can node @@ -473,68 +365,32 @@ pub fn pushTypeVar(self: *CIR, content: types.Content, parent_node_idx: Node.Idx /// Associates a type with an existing definition node. /// -/// **Usage**: After creating a definition (like a function or variable declaration), -/// use this to set its concrete type based on type inference or annotation. -/// -/// **Example**: Setting the final inferred type of a function definition. +/// Use this to set the concrete type of a definition after type inference. pub fn setTypeVarAtDef(self: *CIR, at_idx: Def.Idx, content: types.Content) types.Var { return self.setTypeVarAt(@enumFromInt(@intFromEnum(at_idx)), content); } /// Associates a type with an existing expression node. /// -/// **Usage**: After creating an expression, use this to set its final type. -/// This is commonly done after type inference determines the concrete type. -/// -/// **Example**: For integer literal, after creating the expression with type variables, -/// set the final unified type: -/// ```zig -/// _ = self.can_ir.setTypeVarAtExpr(expr_idx, -/// Content{ .structure = .{ .num = .{ .num_poly = int_var } } }); -/// ``` -/// -/// **Record Special Case**: For records, you must ALSO set the type on the record_var: -/// ```zig -/// // Both assignments are required for records: -/// _ = self.setTypeVarAt(@enumFromInt(@intFromEnum(record_type_var)), record_content); -/// _ = self.setTypeVarAtExpr(expr_idx, record_content); -/// ``` +/// Use this to set the final type of an expression after type inference. pub fn setTypeVarAtExpr(self: *CIR, at_idx: Expr.Idx, content: types.Content) types.Var { return self.setTypeVarAt(@enumFromInt(@intFromEnum(at_idx)), content); } /// Associates a type with an existing pattern node. /// -/// **Usage**: After creating a pattern (like in function parameters or destructuring), -/// use this to set its type based on inference or context. -/// -/// **Example**: Setting the type of a pattern in a function parameter after -/// the function's type signature is known. +/// Use this to set the type of a pattern after type inference or from context. pub fn setTypeVarAtPat(self: *CIR, at_idx: Pattern.Idx, content: types.Content) types.Var { return self.setTypeVarAt(@enumFromInt(@intFromEnum(at_idx)), content); } /// Core function that associates a type with any existing CIR node. /// -/// This is the fundamental operation used by all the `setTypeVarAt*` functions. -/// It takes a node index (which corresponds to a type variable index) and -/// sets the type content for that variable in the types store. +/// This is used by all the `setTypeVarAt*` wrapper functions. Node indices +/// correspond directly to type variable indices, allowing direct conversion. +/// Usually called indirectly through the typed wrappers rather than directly. /// -/// **Usage**: Usually called indirectly through the typed wrapper functions -/// (`setTypeVarAtExpr`, `setTypeVarAtDef`, etc.) rather than directly. -/// -/// **Direct Usage**: Needed for record canonicalization to set types on record_var: -/// ```zig -/// _ = self.setTypeVarAt(@enumFromInt(@intFromEnum(record_type_var)), record_content); -/// ``` -/// -/// **How it works**: -/// 1. Converts the node index to a type variable index (they use the same numbering) -/// 2. Ensures the types store has enough slots (backfilling if needed) -/// 3. Sets the type content at that slot -/// -/// **Note**: This assumes the node was already created - it doesn't create new nodes, -/// only associates types with existing ones. +/// **Note**: The node must already exist - this only sets types, not create nodes. pub fn setTypeVarAt(self: *CIR, at_idx: Node.Idx, content: types.Content) types.Var { // if the new can node idx is greater than the types store length, backfill const var_: types.Var = @enumFromInt(@intFromEnum(at_idx));