mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-09 02:54:49 +00:00
improve doc comments
This commit is contained in:
parent
9b5e4989f7
commit
5b9e44fdb2
1 changed files with 17 additions and 161 deletions
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue