More fixes

This commit is contained in:
Richard Feldman 2025-11-08 11:21:40 -05:00
parent f3a848fa3b
commit 1fe4fd6971
No known key found for this signature in database
12 changed files with 240 additions and 71 deletions

View file

@ -489,7 +489,7 @@ fn processTypeDeclFirstPass(
const module_name_text = self.env.getIdent(self.env.module_name_idx);
// Check if the qualified name matches the module name (with or without .roc extension)
const is_main_type = std.mem.eql(u8, qualified_name_text, module_name_text) or
(std.mem.endsWith(u8, module_name_text, ".roc") and std.mem.eql(u8, qualified_name_text, module_name_text[0..module_name_text.len-4]));
(std.mem.endsWith(u8, module_name_text, ".roc") and std.mem.eql(u8, qualified_name_text, module_name_text[0 .. module_name_text.len - 4]));
if (is_main_type) {
// This is the main type of the type module - set its node index
const node_idx_u16 = @as(u16, @intCast(@intFromEnum(type_decl_stmt_idx)));
@ -1304,12 +1304,12 @@ pub fn canonicalizeFile(
self.env.all_defs = try self.env.store.defSpanFrom(scratch_defs_start);
self.env.all_statements = try self.env.store.statementSpanFrom(scratch_statements_start);
_ = self.env.store.sliceDefs(self.env.all_defs).len;
_ = self.env.store.sliceDefs(self.env.all_defs).len;
// For Type Modules, transform annotation-only defs into hosted lambdas (in-place)
// This allows platforms to import these modules and use the hosted functions
// Only do this when building platform modules (i.e., when root module is a platform)
std.debug.print("DEBUG: canDefinitions called for module: {s}, kind={s}, building_platform={}\n", .{self.env.module_name, @tagName(self.env.module_kind), self.env.building_platform_modules});
std.debug.print("DEBUG: canDefinitions called for module: {s}, kind={s}, building_platform={}\n", .{ self.env.module_name, @tagName(self.env.module_kind), self.env.building_platform_modules });
// Debug: Print all defs for app modules
const is_test_module = std.mem.eql(u8, self.env.module_name, "stdin_simple.roc") or std.mem.eql(u8, self.env.module_name, "app.roc");
@ -1319,18 +1319,18 @@ pub fn canonicalizeFile(
for (all_defs, 0..) |def_idx, def_i| {
const def = self.env.store.getDef(def_idx);
const expr = self.env.store.getExpr(def.expr);
std.debug.print(" def[{}] expr_idx={} expr_tag={s}\n", .{def_i, @intFromEnum(def.expr), @tagName(expr)});
std.debug.print(" def[{}] expr_idx={} expr_tag={s}\n", .{ def_i, @intFromEnum(def.expr), @tagName(expr) });
// If it's a lambda, show its body
if (expr == .e_lambda) {
const body_expr = self.env.store.getExpr(expr.e_lambda.body);
std.debug.print(" lambda body: expr_idx={} expr_tag={s}\n", .{@intFromEnum(expr.e_lambda.body), @tagName(body_expr)});
std.debug.print(" lambda body: expr_idx={} expr_tag={s}\n", .{ @intFromEnum(expr.e_lambda.body), @tagName(body_expr) });
// If the body is a call, show what it's calling
if (body_expr == .e_call) {
const func_expr = self.env.store.getExpr(body_expr.e_call.func);
const call_args = self.env.store.sliceExpr(body_expr.e_call.args);
std.debug.print(" call func: expr_idx={} expr_tag={s} num_args={}\n", .{@intFromEnum(body_expr.e_call.func), @tagName(func_expr), call_args.len});
std.debug.print(" call func: expr_idx={} expr_tag={s} num_args={}\n", .{ @intFromEnum(body_expr.e_call.func), @tagName(func_expr), call_args.len });
}
}
}
@ -1381,7 +1381,7 @@ pub fn canonicalizeFile(
const def_idx = try self.env.addDef(.{
.pattern = pattern_idx,
.expr = expr_idx,
.annotation = null, // Type annotations on Type Module items aren't stored as CIR annotations
.annotation = null, // Type annotations on Type Module items aren't stored as CIR annotations
.kind = .let,
}, base.Region.zero());
@ -1403,13 +1403,11 @@ pub fn canonicalizeFile(
try self.env.store.addScratchDef(def_idx);
}
self.env.all_defs = try self.env.store.defSpanFrom(combined_start);
}
var modified_def_indices = try HostedCompiler.replaceAnnoOnlyWithHosted(self.env);
defer modified_def_indices.deinit(self.env.gpa);
// If we transformed any annotation-only defs, collect and sort them to assign indices
if (modified_def_indices.items.len > 0) {
var sorted_fns = try HostedCompiler.collectAndSortHostedFunctions(self.env);
@ -3801,9 +3799,7 @@ pub fn canonicalizeExpr(
}
// Regular field access canonicalization
return CanonicalizedExpr{
.idx = (try self.canonicalizeRegularFieldAccess(field_access)) orelse return null,
.free_vars = null };
return CanonicalizedExpr{ .idx = (try self.canonicalizeRegularFieldAccess(field_access)) orelse return null, .free_vars = null };
},
.local_dispatch => |local_dispatch| {
// local_dispatch is structurally identical to field_access (both are BinOp)
@ -8770,8 +8766,7 @@ fn tryModuleQualifiedLookup(self: *Self, field_access: AST.BinOp) std.mem.Alloca
} else null;
// If we didn't find a valid node index, return null to fall through to error handling
if (target_node_idx_opt == null) {
}
if (target_node_idx_opt == null) {}
const target_node_idx = target_node_idx_opt orelse return null;
// Create the e_lookup_external expression with Import.Idx

View file

@ -389,7 +389,7 @@ pub const Expr = union(enum) {
/// ```
e_hosted_lambda: struct {
symbol_name: base.Ident.Idx,
index: u32, // Index into RocOps.hosted_fns (assigned during canonicalization)
index: u32, // Index into RocOps.hosted_fns (assigned during canonicalization)
args: CIR.Pattern.Span,
body: Expr.Idx,
},

View file

@ -39,7 +39,7 @@ pub fn replaceAnnoOnlyWithHosted(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
if (pattern == .assign) {
const ident = pattern.assign.ident;
const ident_name = env.getIdent(ident);
std.debug.print("DEBUG: Processing anno-only def: {s}, has annotation: {}\n", .{ident_name, def.annotation != null});
std.debug.print("DEBUG: Processing anno-only def: {s}, has annotation: {}\n", .{ ident_name, def.annotation != null });
// Extract the number of arguments from the annotation
const annotation = env.store.getAnnotation(def.annotation.?);
@ -96,12 +96,14 @@ pub fn replaceAnnoOnlyWithHosted(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
}
// Create e_hosted_lambda expression
const expr_idx = try env.addExpr(.{ .e_hosted_lambda = .{
.symbol_name = ident,
.index = 0, // Placeholder; will be assigned during sorting pass
.args = args_span,
.body = body_idx,
} }, base.Region.zero());
const expr_idx = try env.addExpr(.{
.e_hosted_lambda = .{
.symbol_name = ident,
.index = 0, // Placeholder; will be assigned during sorting pass
.args = args_span,
.body = body_idx,
},
}, base.Region.zero());
// Ensure types array has an entry for this new expression
const expr_int = @intFromEnum(expr_idx);
@ -135,7 +137,7 @@ pub fn replaceAnnoOnlyWithHosted(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
pub const HostedFunctionInfo = struct {
symbol_name: base.Ident.Idx,
expr_idx: CIR.Expr.Idx,
name_text: []const u8, // For sorting
name_text: []const u8, // For sorting
};
/// Collect all hosted functions from the module (transitively through imports)
@ -159,7 +161,7 @@ pub fn collectAndSortHostedFunctions(env: *ModuleEnv) !std.ArrayList(HostedFunct
if (std.mem.endsWith(u8, module_name, ".roc")) {
module_name = module_name[0 .. module_name.len - 4];
}
const qualified_name = try std.fmt.allocPrint(env.gpa, "{s}.{s}", .{module_name, local_name});
const qualified_name = try std.fmt.allocPrint(env.gpa, "{s}.{s}", .{ module_name, local_name });
defer env.gpa.free(qualified_name);
// Strip the `!` suffix for sorting (e.g., "Stdout.line!" -> "Stdout.line")
@ -171,7 +173,6 @@ pub fn collectAndSortHostedFunctions(env: *ModuleEnv) !std.ArrayList(HostedFunct
// Allocate a copy for storage
const name_copy = try env.gpa.dupe(u8, stripped_name);
try hosted_fns.append(env.gpa, .{
.symbol_name = hosted.symbol_name,
.expr_idx = def.expr,
@ -189,7 +190,7 @@ pub fn collectAndSortHostedFunctions(env: *ModuleEnv) !std.ArrayList(HostedFunct
std.mem.sort(HostedFunctionInfo, hosted_fns.items, {}, SortContext.lessThan);
for (hosted_fns.items, 0..) |fn_info, i| {
std.debug.print(" [{d}] {s}\n", .{i, fn_info.name_text});
std.debug.print(" [{d}] {s}\n", .{ i, fn_info.name_text });
}
return hosted_fns;

View file

@ -2463,7 +2463,7 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
// Unify this expression with the referenced pattern
},
.e_lookup_external => |ext| {
std.debug.print("DEBUG Check: e_lookup_external module_idx={} node_idx={}\n", .{@intFromEnum(ext.module_idx), ext.target_node_idx});
std.debug.print("DEBUG Check: e_lookup_external module_idx={} node_idx={}\n", .{ @intFromEnum(ext.module_idx), ext.target_node_idx });
if (try self.resolveVarFromExternal(ext.module_idx, ext.target_node_idx)) |ext_ref| {
// Check what type was resolved
const resolved = self.types.resolveVar(ext_ref.local_var);
@ -2728,7 +2728,7 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
// Now, check the call args against the type of function
std.debug.print("DEBUG Check e_call: mb_func is_null={}\n", .{mb_func == null});
if (mb_func) |func| {
std.debug.print(" func.args.len={} call_args.len={}\n", .{self.types.sliceVars(func.args).len, call_arg_expr_idxs.len});
std.debug.print(" func.args.len={} call_args.len={}\n", .{ self.types.sliceVars(func.args).len, call_arg_expr_idxs.len });
const func_args = self.types.sliceVars(func.args);
// Special case: if func has 1 arg that is empty tuple () and call has 0 args, that's valid
@ -3039,7 +3039,7 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, rank: types_mod.Rank, expected
}
},
.e_hosted_lambda => |hosted| {
std.debug.print("DEBUG Check: e_hosted_lambda index={}, has expected={}\n", .{hosted.index, expected == .expected});
std.debug.print("DEBUG Check: e_hosted_lambda index={}, has expected={}\n", .{ hosted.index, expected == .expected });
if (expected == .expected) {
std.debug.print(" Expected type from_annotation={}\n", .{expected.expected.from_annotation});
}

View file

@ -1135,10 +1135,10 @@ fn setupSharedMemorySingleModule(allocs: *Allocators, roc_file_path: []const u8,
// Create a properly aligned header structure
const Header = struct {
parent_base_addr: u64,
module_count: u32, // Number of ModuleEnvs stored
entry_count: u32, // Number of entry points
module_count: u32, // Number of ModuleEnvs stored
entry_count: u32, // Number of entry points
def_indices_offset: u64,
module_envs_offset: u64, // Offset to array of module env offsets
module_envs_offset: u64, // Offset to array of module env offsets
};
const header_ptr = try shm_allocator.create(Header);
@ -1402,7 +1402,7 @@ fn setupSharedMemoryMultiModule(allocs: *Allocators, app_file_path: []const u8,
if (std.mem.endsWith(u8, module_name, ".roc")) {
module_name = module_name[0 .. module_name.len - 4];
}
const qualified_name = try std.fmt.allocPrint(allocs.gpa, "{s}.{s}", .{module_name, local_name});
const qualified_name = try std.fmt.allocPrint(allocs.gpa, "{s}.{s}", .{ module_name, local_name });
defer allocs.gpa.free(qualified_name);
const stripped_name = if (std.mem.endsWith(u8, qualified_name, "!"))
@ -1651,19 +1651,31 @@ fn compileAppModuleToSharedMemory(
for (env.imports.imports.items.items[1..], 1..) |import_str_idx, i| {
const import_name = env.common.getString(import_str_idx);
// Match to one of the platform modules dynamically
// Extract the module name from the import (e.g., "pf.Stdout" -> "Stdout")
const import_module_name = if (std.mem.lastIndexOf(u8, import_name, ".")) |dot_idx|
import_name[dot_idx + 1..]
// First strip .roc extension if present (e.g., "Stdout.roc" -> "Stdout")
const without_ext = if (std.mem.endsWith(u8, import_name, ".roc"))
import_name[0 .. import_name.len - 4]
else
import_name;
// Then extract the module name from the import (e.g., "pf.Stdout" -> "Stdout")
const import_module_name = if (std.mem.lastIndexOf(u8, without_ext, ".")) |dot_idx|
without_ext[dot_idx + 1 ..]
else
without_ext;
// Find matching platform module
var found_match = false;
for (platform_envs) |platform_env| {
const platform_module_name = platform_env.module_name;
// Match "Stdout" to "Stdout.roc", etc.
if (std.mem.indexOf(u8, platform_module_name, import_module_name) != null) {
// Strip .roc extension if present for exact matching
const name_without_ext = if (std.mem.endsWith(u8, platform_module_name, ".roc"))
platform_module_name[0 .. platform_module_name.len - 4]
else
platform_module_name;
// Match "Stdout" to "Stdout.roc" via exact match, not substring
if (std.mem.eql(u8, name_without_ext, import_module_name)) {
imported_modules_slice[i] = platform_env;
found_match = true;
break;

View file

@ -191,18 +191,30 @@ pub const Interpreter = struct {
}
} else {
// Dynamically match any platform module
// Extract module name from import (e.g., "pf.Stdout" -> "Stdout")
const module_name = if (std.mem.lastIndexOf(u8, import_name, ".")) |dot_idx|
import_name[dot_idx + 1..]
// First strip .roc extension if present (e.g., "Stdout.roc" -> "Stdout")
const without_ext = if (std.mem.endsWith(u8, import_name, ".roc"))
import_name[0 .. import_name.len - 4]
else
import_name;
// Then extract the module name from the import (e.g., "pf.Stdout" -> "Stdout")
const module_name = if (std.mem.lastIndexOf(u8, without_ext, ".")) |dot_idx|
without_ext[dot_idx + 1 ..]
else
without_ext;
// Find matching platform module by searching through all other_envs
for (other_envs) |platform_env| {
const platform_module_name = platform_env.module_name;
// Match "Stdout" to "Stdout.roc", etc.
if (std.mem.indexOf(u8, platform_module_name, module_name) != null) {
// Strip .roc extension if present for exact matching
const name_without_ext = if (std.mem.endsWith(u8, platform_module_name, ".roc"))
platform_module_name[0 .. platform_module_name.len - 4]
else
platform_module_name;
// Match "Stdout" to "Stdout.roc" via exact match, not substring
if (std.mem.eql(u8, name_without_ext, module_name)) {
matched_module = platform_env;
break;
}
@ -384,10 +396,9 @@ pub const Interpreter = struct {
defer self.trimBindingList(&self.bindings, base_binding_len, roc_ops);
const result_value = try self.evalExprMinimal(header.body_idx, roc_ops, null);
// TEMPORARY: Comment out decref to debug
// defer result_value.decref(&self.runtime_layout_store, roc_ops);
defer result_value.decref(&self.runtime_layout_store, roc_ops);
_ = result_value;
try result_value.copyToPtr(&self.runtime_layout_store, ret_ptr, roc_ops);
return;
}
@ -1724,7 +1735,7 @@ pub const Interpreter = struct {
const params = self.env.store.slicePatterns(header.params);
if (params.len != arg_indices.len) {
std.debug.print("ERROR: TypeMismatch at line 1658 - params.len={} != arg_indices.len={}\n", .{params.len, arg_indices.len});
std.debug.print("ERROR: TypeMismatch at line 1658 - params.len={} != arg_indices.len={}\n", .{ params.len, arg_indices.len });
return error.TypeMismatch;
}
// Provide closure context for capture lookup during body eval
@ -1752,7 +1763,7 @@ pub const Interpreter = struct {
const lambda = func_expr.e_lambda;
const params = self.env.store.slicePatterns(lambda.args);
if (params.len != arg_indices.len) {
std.debug.print("ERROR: TypeMismatch at line 1686 - params.len={} != arg_indices.len={}\n", .{params.len, arg_indices.len});
std.debug.print("ERROR: TypeMismatch at line 1686 - params.len={} != arg_indices.len={}\n", .{ params.len, arg_indices.len });
return error.TypeMismatch;
}
var bind_count: usize = 0;
@ -2095,7 +2106,7 @@ pub const Interpreter = struct {
// Print all keys in import_envs
var iter = self.import_envs.iterator();
while (iter.next()) |entry| {
std.debug.print(" import_envs key: {} -> env {*}\n", .{entry.key_ptr.*, entry.value_ptr.*});
std.debug.print(" import_envs key: {} -> env {*}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
// Cross-module reference - look up in imported module
@ -2104,7 +2115,7 @@ pub const Interpreter = struct {
return error.NotImplemented;
};
std.debug.print("Found module env: {*} (module: {s})\n", .{other_env, other_env.module_name});
std.debug.print("Found module env: {*} (module: {s})\n", .{ other_env, other_env.module_name });
// Check what type of node this is by using the store's method
const is_def = other_env.store.isDefNode(lookup.target_node_idx);

View file

@ -30,7 +30,7 @@ fn stderrTraceWriter() *std.Io.Writer {
var shared_memory_initialized: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
var global_shm: ?SharedMemoryAllocator = null;
var global_env_ptr: ?*ModuleEnv = null;
var global_module_envs: ?[]const *ModuleEnv = null; // All loaded module envs
var global_module_envs: ?[]const *ModuleEnv = null; // All loaded module envs
var shm_mutex: std.Thread.Mutex = .{};
const CIR = can.CIR;
const ModuleEnv = can.ModuleEnv;
@ -46,10 +46,10 @@ const MODULE_ENV_OFFSET = 0x10; // 8 bytes for u64, 4 bytes for u32, 4 bytes pad
// Header structure that matches the one in main.zig
const Header = struct {
parent_base_addr: u64,
module_count: u32, // Number of ModuleEnvs stored
entry_count: u32, // Number of entry points
module_count: u32, // Number of ModuleEnvs stored
entry_count: u32, // Number of entry points
def_indices_offset: u64,
module_envs_offset: u64, // Offset to array of module env offsets
module_envs_offset: u64, // Offset to array of module env offsets
};
/// Comprehensive error handling for the shim
@ -77,7 +77,7 @@ const ShimError = error{
export fn roc_entrypoint(entry_idx: u32, ops: *builtins.host_abi.RocOps, ret_ptr: *anyopaque, arg_ptr: ?*anyopaque) callconv(.c) void {
evaluateFromSharedMemory(entry_idx, ops, ret_ptr, arg_ptr) catch |err| {
var buf: [512]u8 = undefined;
const msg2 = std.fmt.bufPrint(&buf, "Error evaluating from shared memory: {s} (entry_idx={})", .{@errorName(err), entry_idx}) catch "Error evaluating from shared memory";
const msg2 = std.fmt.bufPrint(&buf, "Error evaluating from shared memory: {s} (entry_idx={})", .{ @errorName(err), entry_idx }) catch "Error evaluating from shared memory";
ops.crash(msg2);
};
}
@ -161,7 +161,6 @@ fn evaluateFromSharedMemory(entry_idx: u32, roc_ops: *RocOps, ret_ptr: *anyopaqu
const def = env_ptr.store.getDef(def_idx);
const expr_idx = def.expr;
// Evaluate the expression (with optional arguments)
std.debug.print("TRACE roc_entrypoint: About to evaluate expression\n", .{});
interpreter.evaluateExpression(expr_idx, ret_ptr, roc_ops, arg_ptr) catch |err| {
@ -227,23 +226,18 @@ fn setupModuleEnv(shm: *SharedMemoryAllocator, roc_ops: *RocOps) ShimError!*Modu
const env_addr = @intFromPtr(base_ptr) + @as(usize, @intCast(module_env_offsets[i]));
const serialized_ptr: *ModuleEnv.Serialized = @ptrFromInt(env_addr);
std.debug.print(" Loading module {}: offset=0x{x}\n", .{i, module_env_offsets[i]});
std.debug.print(" Loading module {}: offset=0x{x}\n", .{ i, module_env_offsets[i] });
// Extract and relocate module_name from serialized data
const module_name_slice_parts = serialized_ptr.module_name;
const module_name_ptr = @as(usize, @intCast(module_name_slice_parts[0]));
const module_name_len = @as(usize, @intCast(module_name_slice_parts[1]));
std.debug.print(" Raw module_name_ptr: 0x{x}, len: {}\n", .{module_name_ptr, module_name_len});
std.debug.print(" Raw module_name_ptr: 0x{x}, len: {}\n", .{ module_name_ptr, module_name_len });
const relocated_module_name_ptr = if (module_name_ptr > 0)
@as([*]const u8, @ptrFromInt(@as(usize, @intCast(@as(isize, @intCast(module_name_ptr)) + offset))))
else
@as([*]const u8, @ptrFromInt(@as(usize, 0)));
const module_name = if (module_name_len > 0 and module_name_ptr > 0)
relocated_module_name_ptr[0..module_name_len]
else
"";
const module_name = if (module_name_len > 0 and module_name_ptr > 0) blk: {
const relocated_module_name_ptr = @as([*]const u8, @ptrFromInt(@as(usize, @intCast(@as(isize, @intCast(module_name_ptr)) + offset))));
break :blk relocated_module_name_ptr[0..module_name_len];
} else "";
// Deserialize the ModuleEnv with the relocated module_name
// Empty string is used for source since it's not needed in the interpreter
@ -301,10 +295,10 @@ fn createInterpreter(env_ptr: *ModuleEnv, roc_ops: *RocOps) ShimError!Interprete
} else &[_]*const can.ModuleEnv{builtin_modules.builtin_module.env};
std.debug.print("=== SHIM: Creating interpreter ===\n", .{});
std.debug.print("Primary env: {*} (module: {s})\n", .{env_ptr, env_ptr.module_name});
std.debug.print("Primary env: {*} (module: {s})\n", .{ env_ptr, env_ptr.module_name });
std.debug.print("Other envs count: {}\n", .{other_envs.len});
for (other_envs, 0..) |other_env, i| {
std.debug.print(" Other env {}: {*} (module: {s})\n", .{i, other_env, other_env.module_name});
std.debug.print(" Other env {}: {*} (module: {s})\n", .{ i, other_env, other_env.module_name });
}
const interpreter = eval.Interpreter.init(allocator, env_ptr, builtin_types, other_envs) catch {

View file

@ -20,7 +20,28 @@ result = Foo.transform(Foo.defaultBar)
# EXPECTED
NIL
# PROBLEMS
NIL
**MISSING NESTED TYPE**
`Foo` is in scope, but it doesn't have a nested type named `Bar`.
It's referenced here:
**nominal_associated_lookup_mixed.md:6:17:6:24:**
```roc
transform : Foo.Bar -> Foo.Bar
```
^^^^^^^
**MISSING NESTED TYPE**
`Foo` is in scope, but it doesn't have a nested type named `Bar`.
It's referenced here:
**nominal_associated_lookup_mixed.md:6:28:6:35:**
```roc
transform : Foo.Bar -> Foo.Bar
```
^^^^^^^
# TOKENS
~~~zig
UpperIdent,OpColonEqual,OpenSquare,UpperIdent,CloseSquare,Dot,OpenCurly,
@ -89,6 +110,13 @@ result = Foo.transform(Foo.defaultBar)
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "transform"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-malformed)
(ty-malformed))))
(d-let
(p-assign (ident "result"))
(e-call
@ -128,6 +156,7 @@ result = Foo.transform(Foo.defaultBar)
~~~clojure
(inferred-types
(defs
(patt (type "Error"))
(patt (type "Foo.Bar"))
(patt (type "Foo.Bar"))
(patt (type "Foo.Bar -> Foo.Bar")))
@ -137,6 +166,7 @@ result = Foo.transform(Foo.defaultBar)
(nominal (type "Foo.Bar")
(ty-header (name "Foo.Bar"))))
(expressions
(expr (type "Error"))
(expr (type "Foo.Bar"))
(expr (type "Foo.Bar"))
(expr (type "Foo.Bar -> Foo.Bar"))))

View file

@ -23,7 +23,39 @@ external = Foo.defaultBar
# EXPECTED
NIL
# PROBLEMS
NIL
**UNDECLARED TYPE**
The type _Bar_ is not declared in this scope.
This type is referenced here:
**nominal_associated_self_reference.md:4:18:4:21:**
```roc
defaultBar : Bar
```
^^^
**UNDECLARED TYPE**
The type _Bar_ is not declared in this scope.
This type is referenced here:
**nominal_associated_self_reference.md:7:17:7:20:**
```roc
transform : Bar -> Bar
```
^^^
**UNDECLARED TYPE**
The type _Bar_ is not declared in this scope.
This type is referenced here:
**nominal_associated_self_reference.md:7:24:7:27:**
```roc
transform : Bar -> Bar
```
^^^
# TOKENS
~~~zig
UpperIdent,OpColonEqual,OpenSquare,UpperIdent,CloseSquare,Dot,OpenCurly,
@ -101,6 +133,18 @@ external = Foo.defaultBar
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "defaultBar"))
(e-anno-only)
(annotation
(ty-malformed)))
(d-let
(p-assign (ident "transform"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-malformed)
(ty-malformed))))
(d-let
(p-assign (ident "external"))
(e-lookup-local
@ -145,6 +189,8 @@ external = Foo.defaultBar
~~~clojure
(inferred-types
(defs
(patt (type "Error"))
(patt (type "Error"))
(patt (type "Foo.Bar"))
(patt (type "Foo.Bar"))
(patt (type "Foo.Bar -> Foo.Bar"))
@ -155,6 +201,8 @@ external = Foo.defaultBar
(nominal (type "Foo.Bar")
(ty-header (name "Foo.Bar"))))
(expressions
(expr (type "Error"))
(expr (type "Error"))
(expr (type "Foo.Bar"))
(expr (type "Foo.Bar"))
(expr (type "Foo.Bar -> Foo.Bar"))

View file

@ -317,6 +317,36 @@ main = {
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "to_str"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Adv") (local))
(ty-lookup (name "Str") (builtin)))))
(d-let
(p-assign (ident "to_u64"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Adv") (local))
(ty-lookup (name "U64") (builtin)))))
(d-let
(p-assign (ident "update_str"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Adv") (local))
(ty-lookup (name "Str") (builtin))
(ty-lookup (name "Adv") (local)))))
(d-let
(p-assign (ident "update_u64"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Adv") (local))
(ty-lookup (name "U64") (builtin))
(ty-lookup (name "Adv") (local)))))
(d-let
(p-assign (ident "mismatch"))
(e-block
@ -496,6 +526,10 @@ main = {
~~~clojure
(inferred-types
(defs
(patt (type "Error"))
(patt (type "Error"))
(patt (type "Error"))
(patt (type "Error"))
(patt (type "_a"))
(patt (type "_a"))
(patt (type "_a"))
@ -508,6 +542,10 @@ main = {
(nominal (type "Adv")
(ty-header (name "Adv"))))
(expressions
(expr (type "Error"))
(expr (type "Error"))
(expr (type "Error"))
(expr (type "Error"))
(expr (type "_a"))
(expr (type "_a"))
(expr (type "_a"))

View file

@ -167,6 +167,20 @@ main = (helper1(val), helper2(val))
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "to_str"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Basic") (local))
(ty-lookup (name "Str") (builtin)))))
(d-let
(p-assign (ident "to_str2"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Basic") (local))
(ty-lookup (name "Str") (builtin)))))
(d-let
(p-assign (ident "helper1"))
(e-lambda
@ -271,6 +285,8 @@ main = (helper1(val), helper2(val))
~~~clojure
(inferred-types
(defs
(patt (type "Error"))
(patt (type "Error"))
(patt (type "a -> b where [a.to_str : a -> b]"))
(patt (type "a -> b where [a.to_str2 : a -> b]"))
(patt (type "Basic"))
@ -281,6 +297,8 @@ main = (helper1(val), helper2(val))
(nominal (type "Basic")
(ty-header (name "Basic"))))
(expressions
(expr (type "Error"))
(expr (type "Error"))
(expr (type "a -> b where [a.to_str : a -> b]"))
(expr (type "a -> b where [a.to_str2 : a -> b]"))
(expr (type "Basic"))

View file

@ -231,6 +231,24 @@ NO CHANGE
# CANONICALIZE
~~~clojure
(can-ir
(d-let
(p-assign (ident "get_value"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Container") (local))
(ty-lookup (name "Str") (builtin)))))
(d-let
(p-assign (ident "transform"))
(e-anno-only)
(annotation
(ty-fn (effectful false)
(ty-lookup (name "Container") (local))
(ty-parens
(ty-fn (effectful false)
(ty-lookup (name "Str") (builtin))
(ty-lookup (name "Str") (builtin))))
(ty-lookup (name "Container") (local)))))
(d-let
(p-assign (ident "extract"))
(e-lambda
@ -402,6 +420,8 @@ NO CHANGE
~~~clojure
(inferred-types
(defs
(patt (type "Error"))
(patt (type "Error"))
(patt (type "a -> Str where [a.get_value : a -> Str]"))
(patt (type "a, Str -> Str -> a where [a.transform : a, Str -> Str -> aa.transform : a, Str -> Str -> a]"))
(patt (type "Container"))
@ -416,6 +436,8 @@ NO CHANGE
(nominal (type "Container")
(ty-header (name "Container"))))
(expressions
(expr (type "Error"))
(expr (type "Error"))
(expr (type "a -> Str where [a.get_value : a -> Str]"))
(expr (type "a, Str -> Str -> a where [a.transform : a, Str -> Str -> aa.transform : a, Str -> Str -> a]"))
(expr (type "Container"))