Typos + snapshot problems

This commit is contained in:
Jared Ramirez 2025-10-21 08:54:29 -04:00
parent d2fbc1c07c
commit 8ea4eaa488
No known key found for this signature in database
GPG key ID: 41158983F521D68C
7 changed files with 164 additions and 52 deletions

View file

@ -603,7 +603,7 @@ fn processAssociatedItemsSecondPass(
// then just add the annotation independently
// TODO: Capture diagnostic that this anno doesn't
// have a corrosponding def
// have a corresponding def
const region = self.parse_ir.tokenizedRegionToRegion(ta.region);
@ -1030,7 +1030,7 @@ pub fn canonicalizeFile(
// then just add the annotation independently
// TODO: Capture diagnostic that this anno doesn't
// have a corrosponding def
// have a corresponding def
const type_anno_stmt = Statement{ .s_type_anno = .{
.name = name_ident,
@ -6399,7 +6399,7 @@ pub fn canonicalizeBlockStatement(self: *Self, ast_stmt: AST.Statement, ast_stmt
// then just add the annotation independently
// TODO: Capture diagnostic that this anno doesn't
// have a corrosponding def
// have a corresponding def
const stmt_idx = try self.env.addStatement(Statement{
.s_type_anno = .{
@ -6416,7 +6416,7 @@ pub fn canonicalizeBlockStatement(self: *Self, ast_stmt: AST.Statement, ast_stmt
// then just add the annotation independently
// TODO: Capture diagnostic that this anno doesn't
// have a corrosponding def
// have a corresponding def
const stmt_idx = try self.env.addStatement(Statement{
.s_type_anno = .{

View file

@ -152,7 +152,7 @@ pub const Statement = union(enum) {
///
/// Typically an annotation will be stored on the `Def` and will not be
/// in the tree independently. But if there is an annotation without a
/// corrosponding we represent it with this node
/// corresponding we represent it with this node
s_type_anno: struct {
name: Ident.Idx,
anno: CIR.TypeAnno.Idx,

View file

@ -96,7 +96,7 @@ static_dispatch_method_name_buf: std.ArrayList(u8),
/// A map of rigid variables that we build up during a branch of type checking
const FreeVar = struct { ident: base.Ident.Idx, var_: Var };
/// A struct scratch info aout a static dispatch constaint
/// A struct scratch info about a static dispatch constraint
const ScratchStaticDispatchConstraint = struct {
var_: Var,
constraint: types_mod.StaticDispatchConstraint,
@ -669,7 +669,7 @@ pub fn checkFile(self: *Self) std.mem.Allocator.Error!void {
}
// Check any accumulated static dispatch constraints
try self.checkDeferredStaticDispatchContraints();
try self.checkDeferredStaticDispatchConstraints();
}
// repl //
@ -703,7 +703,7 @@ pub fn checkExprRepl(self: *Self, expr_idx: CIR.Expr.Idx) std.mem.Allocator.Erro
try self.generalizer.generalize(&self.var_pool, rank);
// Check any accumulated static dispatch constraints
try self.checkDeferredStaticDispatchContraints();
try self.checkDeferredStaticDispatchConstraints();
}
// defs //
@ -727,9 +727,6 @@ fn checkDef(self: *Self, def_idx: CIR.Def.Idx) std.mem.Allocator.Error!void {
// Initially set to be flex
const placeholder_ptrn_var = ModuleEnv.varFrom(def.pattern);
var type_writer = try self.cir.initTypeWriter();
defer type_writer.deinit();
// Check the pattern
//
// Generate a fresh variable, because the original pattern has a placeholder
@ -780,9 +777,6 @@ fn generateStmtTypeDeclType(
const decl = self.cir.store.getStatement(decl_idx);
const decl_var = ModuleEnv.varFrom(decl_idx);
var type_writer = try self.cir.initTypeWriter();
defer type_writer.deinit();
switch (decl) {
.s_alias_decl => |alias| {
// Get the type header's args
@ -946,14 +940,14 @@ fn generateAnnotationType(self: *Self, annotation_idx: CIR.Annotation.Idx) std.m
_ = try self.types.setVarRedirect(ModuleEnv.varFrom(annotation_idx), ModuleEnv.varFrom(annotation.anno));
}
/// Given a where clause, generate static dispatchs constraints and add to scratch_static_dispatch_constraints
/// Given a where clause, generate static dispatch constraints and add to scratch_static_dispatch_constraints
fn generateStaticDispatchConstraintFromWhere(self: *Self, where_idx: CIR.WhereClause.Idx) std.mem.Allocator.Error!void {
const where = self.cir.store.getWhereClause(where_idx);
const where_region = self.cir.store.getNodeRegion(ModuleEnv.nodeIdxFrom(where_idx));
switch (where) {
.w_method => |method| {
// Generate type of the thing dispatch reciever
// Generate type of the thing dispatch receiver
try self.generateAnnoTypeInPlace(method.var_, .annotation);
const method_var = ModuleEnv.varFrom(method.var_);
@ -2952,9 +2946,6 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
.e_dot_access => |dot_access| {
// Dot access can either indicate record access or static dispatch
var type_writer = try self.cir.initTypeWriter();
defer type_writer.deinit();
// Check the receiver expression
// E.g. thing.val
// ^^^^^
@ -2975,7 +2966,7 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
// For static dispatch to be used like `thing.dispatch(...)` the
// method being dispatched on must accept the type of `thing` as
// it's first arg. So, we prepend the `reciever_var` to the args list
// it's first arg. So, we prepend the `receiver_var` to the args list
const first_arg_range = try self.types.appendVars(&.{receiver_var});
const rest_args_range = try self.types.appendVars(@ptrCast(dispatch_arg_expr_idxs));
const dispatch_arg_vars_range = Var.SafeList.Range{
@ -2997,13 +2988,13 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
try self.var_pool.addVarToRank(constraint_fn_var, rank);
// Then, create the static dispatch constraint
const contraint = StaticDispatchConstraint{
const constraint = StaticDispatchConstraint{
.fn_name = dot_access.field_name,
.fn_var = constraint_fn_var,
};
const constraint_range = try self.types.appendStaticDispatchConstraints(&.{contraint});
const constraint_range = try self.types.appendStaticDispatchConstraints(&.{constraint});
// Create our constrained flex, and unify it with the reciever
// Create our constrained flex, and unify it with the receiver
const constrained_var = try self.freshFromContent(
.{ .flex = Flex{ .name = null, .constraints = constraint_range } },
rank,
@ -3620,10 +3611,7 @@ fn copyVar(self: *Self, other_module_var: Var, other_module_env: *const ModuleEn
///
/// Initially, we only have to check constraint for `Test.to_str2`. But when we
/// process that, we then have to check `Test.to_str`.
fn checkDeferredStaticDispatchContraints(self: *Self) std.mem.Allocator.Error!void {
var type_writer = try self.cir.initTypeWriter();
defer type_writer.deinit();
fn checkDeferredStaticDispatchConstraints(self: *Self) std.mem.Allocator.Error!void {
var deferred_constraint_len = self.deferred_static_dispatch_constraints.items.items.len;
var deferred_constraint_index: usize = 0;
while (deferred_constraint_index < deferred_constraint_len) : ({
@ -3706,7 +3694,7 @@ fn checkDeferredStaticDispatchContraints(self: *Self) std.mem.Allocator.Error!vo
// Get the def index in the original env
const node_idx_in_original_env = original_env.getExposedNodeIndexById(ident_in_original_env) orelse {
// This can happen if somehow, the orignal module has
// This can happen if somehow, the original module has
// an ident that matches the method/type, but it doesn't
// actually have/expose the method. This should be
// impossible, but we handle it gracefully

View file

@ -18,7 +18,7 @@ const SnapshotContentList = collections.SafeList(SnapshotContent);
const SnapshotContentIdxSafeList = collections.SafeList(SnapshotContentIdx);
const SnapshotRecordFieldSafeList = collections.SafeMultiList(SnapshotRecordField);
const SnapshotTagSafeList = collections.SafeMultiList(SnapshotTag);
const SnapshotStaticDispatchContraintSafeList = collections.SafeList(SnapshotStaticDispatchContraint);
const SnapshotStaticDispatchConstraintSafeList = collections.SafeList(SnapshotStaticDispatchConstraint);
const MkSafeMultiList = collections.SafeMultiList;
const Var = types.Var;
@ -49,13 +49,13 @@ pub const Store = struct {
content_indexes: SnapshotContentIdxSafeList,
record_fields: SnapshotRecordFieldSafeList,
tags: SnapshotTagSafeList,
static_dispatch_constraints: SnapshotStaticDispatchContraintSafeList,
static_dispatch_constraints: SnapshotStaticDispatchConstraintSafeList,
// Scratch
scratch_content: base.Scratch(SnapshotContentIdx),
scratch_tags: base.Scratch(SnapshotTag),
scratch_record_fields: base.Scratch(SnapshotRecordField),
scratch_static_dispatch_constraints: base.Scratch(SnapshotStaticDispatchContraint),
scratch_static_dispatch_constraints: base.Scratch(SnapshotStaticDispatchConstraint),
pub fn initCapacity(gpa: Allocator, capacity: usize) std.mem.Allocator.Error!Self {
return .{
@ -65,11 +65,11 @@ pub const Store = struct {
.content_indexes = try SnapshotContentIdxSafeList.initCapacity(gpa, capacity),
.record_fields = try SnapshotRecordFieldSafeList.initCapacity(gpa, 256),
.tags = try SnapshotTagSafeList.initCapacity(gpa, 256),
.static_dispatch_constraints = try SnapshotStaticDispatchContraintSafeList.initCapacity(gpa, 64),
.static_dispatch_constraints = try SnapshotStaticDispatchConstraintSafeList.initCapacity(gpa, 64),
.scratch_content = try base.Scratch(SnapshotContentIdx).init(gpa),
.scratch_tags = try base.Scratch(SnapshotTag).init(gpa),
.scratch_record_fields = try base.Scratch(SnapshotRecordField).init(gpa),
.scratch_static_dispatch_constraints = try base.Scratch(SnapshotStaticDispatchContraint).init(gpa),
.scratch_static_dispatch_constraints = try base.Scratch(SnapshotStaticDispatchConstraint).init(gpa),
};
}
@ -412,7 +412,7 @@ pub const Store = struct {
self: *Self,
store: *const TypesStore,
range: types.StaticDispatchConstraint.SafeList.Range,
) std.mem.Allocator.Error!SnapshotStaticDispatchContraintSafeList.Range {
) std.mem.Allocator.Error!SnapshotStaticDispatchConstraintSafeList.Range {
const scratch_top = self.scratch_static_dispatch_constraints.top();
defer self.scratch_static_dispatch_constraints.clearFrom(scratch_top);
@ -427,8 +427,8 @@ pub const Store = struct {
self: *Self,
store: *const TypesStore,
constraint: types.StaticDispatchConstraint,
) std.mem.Allocator.Error!SnapshotStaticDispatchContraint {
return SnapshotStaticDispatchContraint{
) std.mem.Allocator.Error!SnapshotStaticDispatchConstraint {
return SnapshotStaticDispatchConstraint{
.fn_name = constraint.fn_name,
.fn_content = try self.deepCopyVar(store, constraint.fn_var),
};
@ -443,7 +443,7 @@ pub const Store = struct {
return self.record_fields.sliceRange(range);
}
pub fn sliceStaticDispatchConstraints(self: *const Self, range: SnapshotStaticDispatchContraintSafeList.Range) SnapshotStaticDispatchContraintSafeList.Slice {
pub fn sliceStaticDispatchConstraints(self: *const Self, range: SnapshotStaticDispatchConstraintSafeList.Range) SnapshotStaticDispatchConstraintSafeList.Slice {
return self.static_dispatch_constraints.sliceRange(range);
}
@ -466,13 +466,13 @@ pub const SnapshotContent = union(enum) {
pub const SnapshotFlex = struct {
name: ?Ident.Idx,
var_: Var,
constraints: SnapshotStaticDispatchContraintSafeList.Range,
constraints: SnapshotStaticDispatchConstraintSafeList.Range,
};
/// TODO
pub const SnapshotRigid = struct {
name: Ident.Idx,
constraints: SnapshotStaticDispatchContraintSafeList.Range,
constraints: SnapshotStaticDispatchConstraintSafeList.Range,
};
/// TODO
@ -558,7 +558,7 @@ pub const SnapshotTag = struct {
};
/// TODO
pub const SnapshotStaticDispatchContraint = struct {
pub const SnapshotStaticDispatchConstraint = struct {
fn_name: Ident.Idx,
fn_content: SnapshotContentIdx, // Index into SnapshotStore.contents
};
@ -590,7 +590,7 @@ pub const SnapshotWriter = struct {
name_counters: std.EnumMap(TypeContext, u32),
flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange),
flex_var_names: std.array_list.Managed(u8),
static_dispatch_constraints: std.array_list.Managed(SnapshotStaticDispatchContraint),
static_dispatch_constraints: std.array_list.Managed(SnapshotStaticDispatchConstraint),
count_seen_idxs: std.array_list.Managed(SnapshotContentIdx),
const FlexVarNameRange = struct { start: usize, end: usize };
@ -607,7 +607,7 @@ pub const SnapshotWriter = struct {
.name_counters = std.EnumMap(TypeContext, u32).init(.{}),
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
.flex_var_names = std.array_list.Managed(u8).init(gpa),
.static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchContraint).init(gpa),
.static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa),
.count_seen_idxs = std.array_list.Managed(SnapshotContentIdx).init(gpa),
};
}
@ -631,7 +631,7 @@ pub const SnapshotWriter = struct {
.name_counters = std.EnumMap(TypeContext, u32).init(.{}),
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
.flex_var_names = std.array_list.Managed(u8).init(gpa),
.static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchContraint).init(gpa),
.static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa),
.count_seen_idxs = try std.array_list.Managed(SnapshotContentIdx).init(gpa),
};
}

View file

@ -1150,7 +1150,7 @@ const DefExpectation = union(enum) {
/// A unified helper to run the full pipeline: parse, canonicalize, and type-check source code.
///
/// Behavior depends on the expectation:
/// Pass: Asserts whole module type checks, and assert the speicifed def matches the expected type string
/// Pass: Asserts whole module type checks, and assert the specified def matches the expected type string
/// Fail: Asserts that there is exactly 1 type error in the module and it's title matches the expected string
fn checkTypesModule(
comptime source_expr: []const u8,

View file

@ -55,7 +55,7 @@ types: *const TypesStore,
idents: *const Ident.Store,
buf: std.array_list.Managed(u8),
seen: std.array_list.Managed(Var),
seen_count_var_occurences: std.array_list.Managed(Var),
seen_count_var_occurrences: std.array_list.Managed(Var),
next_name_index: u32,
name_counters: std.EnumMap(TypeContext, u32),
flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange),
@ -71,7 +71,7 @@ pub fn initFromParts(gpa: std.mem.Allocator, types_store: *const TypesStore, ide
.idents = idents,
.buf = try std.array_list.Managed(u8).initCapacity(gpa, 32),
.seen = try std.array_list.Managed(Var).initCapacity(gpa, 16),
.seen_count_var_occurences = try std.array_list.Managed(Var).initCapacity(gpa, 16),
.seen_count_var_occurrences = try std.array_list.Managed(Var).initCapacity(gpa, 16),
.next_name_index = 0,
.name_counters = std.EnumMap(TypeContext, u32).init(.{}),
.flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa),
@ -84,7 +84,7 @@ pub fn initFromParts(gpa: std.mem.Allocator, types_store: *const TypesStore, ide
pub fn deinit(self: *TypeWriter) void {
self.buf.deinit();
self.seen.deinit();
self.seen_count_var_occurences.deinit();
self.seen_count_var_occurrences.deinit();
self.flex_var_names_map.deinit();
self.flex_var_names.deinit();
self.static_dispatch_constraints.deinit();
@ -94,7 +94,7 @@ pub fn deinit(self: *TypeWriter) void {
pub fn reset(self: *TypeWriter) void {
self.buf.clearRetainingCapacity();
self.seen.clearRetainingCapacity();
self.seen_count_var_occurences.clearRetainingCapacity();
self.seen_count_var_occurrences.clearRetainingCapacity();
self.flex_var_names_map.clearRetainingCapacity();
self.flex_var_names.clearRetainingCapacity();
self.static_dispatch_constraints.clearRetainingCapacity();
@ -821,7 +821,7 @@ pub fn writeFlexVarName(self: *TypeWriter, var_: Var, context: TypeContext, root
/// Count how many times a variable appears in a type
fn countVarOccurrences(self: *TypeWriter, search_var: Var, root_var: Var) std.mem.Allocator.Error!usize {
self.seen_count_var_occurences.clearRetainingCapacity();
self.seen_count_var_occurrences.clearRetainingCapacity();
var count: usize = 0;
try self.countVar(search_var, root_var, &count);
@ -847,13 +847,13 @@ fn countVar(self: *TypeWriter, search_var: Var, current_var: Var, count: *usize)
// Check if we've already seen this var
// This avoids infinite recursion
for (self.seen_count_var_occurences.items) |seen| {
for (self.seen_count_var_occurrences.items) |seen| {
if (seen == resolved.var_) return;
}
// Record that we've seen this var
try self.seen_count_var_occurences.append(resolved.var_);
defer _ = self.seen_count_var_occurences.pop();
try self.seen_count_var_occurrences.append(resolved.var_);
defer _ = self.seen_count_var_occurrences.pop();
// Then recurse
switch (resolved.desc.content) {

View file

@ -25,6 +25,17 @@ mismatch = {
next_val
}
mismatch2 = {
val = Adv.Val(10, "hello")
next_val = val.update_strr(100)
next_val
}
mismatch3 = {
next_val = "Hello".update(100)
next_val
}
main : (Str, U64)
main = {
val = Adv.Val(10, "hello")
@ -34,6 +45,8 @@ main = {
~~~
# EXPECTED
TYPE MISMATCH - Adv.md:17:13:17:32
MISSING METHOD - Adv.md:23:13:23:33
TYPE DOES NOT HAVE METHODS - Adv.md:28:13:28:32
# PROBLEMS
**TYPE MISMATCH**
This expression is used in an unexpected way:
@ -49,6 +62,27 @@ It has the type:
But I expected it to be:
_Adv, Str -> Adv_
**MISSING METHOD**
The **Adv** type does not have a **update_strr** method:
**Adv.md:23:13:23:33:**
```roc
next_val = val.update_strr(100)
```
^^^^^^^^^^^^^^^^^^^^
**Hint:** Did you forget to define **update_strr** in the type's method block?
**TYPE DOES NOT HAVE METHODS**
You're trying to call the `update` method on a `Str`:
**Adv.md:28:13:28:32:**
```roc
next_val = "Hello".update(100)
```
^^^^^^^^^^^^^^^^^^^
But `Str` doesn't support methods.
# TOKENS
~~~zig
UpperIdent,OpColonEqual,OpenSquare,UpperIdent,NoSpaceOpenRound,UpperIdent,Comma,UpperIdent,CloseRound,CloseSquare,Dot,OpenCurly,
@ -66,6 +100,15 @@ LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,Int,Comma,S
LowerIdent,OpAssign,LowerIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,Int,CloseRound,
LowerIdent,
CloseCurly,
LowerIdent,OpAssign,OpenCurly,
LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,Int,Comma,StringStart,StringPart,StringEnd,CloseRound,
LowerIdent,OpAssign,LowerIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,Int,CloseRound,
LowerIdent,
CloseCurly,
LowerIdent,OpAssign,OpenCurly,
LowerIdent,OpAssign,StringStart,StringPart,StringEnd,NoSpaceDotLowerIdent,NoSpaceOpenRound,Int,CloseRound,
LowerIdent,
CloseCurly,
LowerIdent,OpColon,OpenRound,UpperIdent,Comma,UpperIdent,CloseRound,
LowerIdent,OpAssign,OpenCurly,
LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,Int,Comma,StringStart,StringPart,StringEnd,CloseRound,
@ -166,6 +209,38 @@ EndOfFile,
(e-ident (raw "update_str"))
(e-int (raw "100")))))
(e-ident (raw "next_val")))))
(s-decl
(p-ident (raw "mismatch2"))
(e-block
(statements
(s-decl
(p-ident (raw "val"))
(e-apply
(e-tag (raw "Adv.Val"))
(e-int (raw "10"))
(e-string
(e-string-part (raw "hello")))))
(s-decl
(p-ident (raw "next_val"))
(e-field-access
(e-ident (raw "val"))
(e-apply
(e-ident (raw "update_strr"))
(e-int (raw "100")))))
(e-ident (raw "next_val")))))
(s-decl
(p-ident (raw "mismatch3"))
(e-block
(statements
(s-decl
(p-ident (raw "next_val"))
(e-field-access
(e-string
(e-string-part (raw "Hello")))
(e-apply
(e-ident (raw "update"))
(e-int (raw "100")))))
(e-ident (raw "next_val")))))
(s-type-anno (name "main")
(ty-tuple
(ty (name "Str"))
@ -221,6 +296,17 @@ mismatch = {
next_val
}
mismatch2 = {
val = Adv.Val(10, "hello")
next_val = val.update_strr(100)
next_val
}
mismatch3 = {
next_val = "Hello".update(100)
next_val
}
main : (Str, U64)
main = {
val = Adv.Val(10, "hello")
@ -252,6 +338,40 @@ main = {
(e-num (value "100")))))
(e-lookup-local
(p-assign (ident "next_val")))))
(d-let
(p-assign (ident "mismatch2"))
(e-block
(s-let
(p-assign (ident "val"))
(e-nominal (nominal "Adv")
(e-tag (name "Val")
(args
(e-num (value "10"))
(e-string
(e-literal (string "hello")))))))
(s-let
(p-assign (ident "next_val"))
(e-dot-access (field "update_strr")
(receiver
(e-lookup-local
(p-assign (ident "val"))))
(args
(e-num (value "100")))))
(e-lookup-local
(p-assign (ident "next_val")))))
(d-let
(p-assign (ident "mismatch3"))
(e-block
(s-let
(p-assign (ident "next_val"))
(e-dot-access (field "update")
(receiver
(e-string
(e-literal (string "Hello"))))
(args
(e-num (value "100")))))
(e-lookup-local
(p-assign (ident "next_val")))))
(d-let
(p-assign (ident "main"))
(e-block
@ -376,6 +496,8 @@ main = {
~~~clojure
(inferred-types
(defs
(patt (type "_a"))
(patt (type "_a"))
(patt (type "_a"))
(patt (type "(Str, Num(Int(Unsigned64)))"))
(patt (type "Adv -> Str"))
@ -386,6 +508,8 @@ main = {
(nominal (type "Adv")
(ty-header (name "Adv"))))
(expressions
(expr (type "_a"))
(expr (type "_a"))
(expr (type "_a"))
(expr (type "(Str, Num(Int(Unsigned64)))"))
(expr (type "Adv -> Str"))