More let-generalization fixes

This commit is contained in:
Richard Feldman 2025-11-20 09:07:46 -05:00
parent 2ea8cf0c20
commit a28786ce50
No known key found for this signature in database
20 changed files with 416 additions and 328 deletions

View file

@ -1669,6 +1669,23 @@ pub fn addExposedById(self: *Self, ident_idx: Ident.Idx) !void {
/// Associates a node index with an exposed identifier.
pub fn setExposedNodeIndexById(self: *Self, ident_idx: Ident.Idx, node_idx: u16) !void {
// Debug: verify the node is actually a def or statement before storing
// Only validate if the node exists (tests may use dummy indices)
if (node_idx < self.store.nodes.count()) {
const node_idx_enum: CIR.Node.Idx = @enumFromInt(node_idx);
const node = self.store.nodes.get(node_idx_enum);
const ident_name = self.getIdent(ident_idx);
if (node.tag != .def and node.tag != .statement_decl and node.tag != .statement_decl_gen and
node.tag != .statement_nominal_decl and node.tag != .statement_alias_decl) {
std.debug.print("WARNING: setExposedNodeIndexById('{s}', idx={}) - node.tag is {s} (expected def or statement)\n",
.{ ident_name, node_idx, @tagName(node.tag) });
} else {
std.debug.print("DEBUG: setExposedNodeIndexById('{s}', idx={}) - node.tag={s} ✓\n",
.{ ident_name, node_idx, @tagName(node.tag) });
}
}
return try self.common.exposed_items.setNodeIndexById(self.gpa, @bitCast(ident_idx), node_idx);
}

View file

@ -2225,6 +2225,9 @@ pub fn getDef(store: *const NodeStore, def_idx: CIR.Def.Idx) CIR.Def {
const nid: Node.Idx = @enumFromInt(@intFromEnum(def_idx));
const node = store.nodes.get(nid);
if (node.tag != .def) {
std.debug.print("ERROR: getDef() called with idx={}, but node.tag={s} (expected .def)\n", .{ @intFromEnum(def_idx), @tagName(node.tag) });
}
std.debug.assert(node.tag == .def);
const extra_start = node.data_1;

View file

@ -69,6 +69,14 @@ test "NodeStore round trip - Statements" {
},
});
try statements.append(gpa, CIR.Statement{
.s_decl_gen = .{
.pattern = rand_idx(CIR.Pattern.Idx),
.expr = rand_idx(CIR.Expr.Idx),
.anno = rand_idx(CIR.Annotation.Idx),
},
});
try statements.append(gpa, CIR.Statement{
.s_var = .{
.pattern_idx = rand_idx(CIR.Pattern.Idx),

View file

@ -2251,25 +2251,42 @@ pub const Interpreter = struct {
return error.NotImplemented;
};
// The target_node_idx is a Def.Idx in the other module
const target_def_idx: can.CIR.Def.Idx = @enumFromInt(lookup.target_node_idx);
const target_def = other_env.store.getDef(target_def_idx);
// target_node_idx is a raw node index - check what type it is
const node_idx: can.CIR.Node.Idx = @enumFromInt(lookup.target_node_idx);
const node = other_env.store.nodes.get(node_idx);
// Save both env and bindings state
const saved_env = self.env;
const saved_bindings_len = self.bindings.items.len;
self.env = @constCast(other_env);
defer {
self.env = saved_env;
self.bindings.shrinkRetainingCapacity(saved_bindings_len);
std.debug.print("DEBUG e_lookup_external: target_node_idx={}, node.tag={s}\n", .{ lookup.target_node_idx, @tagName(node.tag) });
switch (node.tag) {
.def => {
// It's a def - evaluate the def's expression
const target_def_idx: can.CIR.Def.Idx = @enumFromInt(lookup.target_node_idx);
const target_def = other_env.store.getDef(target_def_idx);
// Save both env and bindings state
const saved_env = self.env;
const saved_bindings_len = self.bindings.items.len;
self.env = @constCast(other_env);
defer {
self.env = saved_env;
self.bindings.shrinkRetainingCapacity(saved_bindings_len);
}
// Evaluate the definition's expression in the other module's context
const result = try self.evalExprMinimal(target_def.expr, roc_ops, expected_rt_var);
return result;
},
.lambda_capture => {
// This indicates a bug - lambda_capture nodes should never be exposed
std.debug.print("ERROR: e_lookup_external incorrectly refers to lambda_capture node (idx={})\n", .{lookup.target_node_idx});
std.debug.print("ERROR: This suggests the exposed node index was set incorrectly during canonicalization\n", .{});
return error.NotImplemented;
},
else => {
std.debug.print("ERROR: e_lookup_external refers to unsupported node type: {s} (idx={})\n", .{ @tagName(node.tag), lookup.target_node_idx });
return error.NotImplemented;
},
}
// Evaluate the definition's expression in the other module's context
// If this is being called as a function, pass through the instantiated type
// from the call site (via expected_rt_var) to avoid re-translating generic types
const result = try self.evalExprMinimal(target_def.expr, roc_ops, expected_rt_var);
return result;
},
.e_unary_minus => |unary| {
const operand_ct_var = can.ModuleEnv.varFrom(unary.expr);

View file

@ -120,6 +120,7 @@ outerFunc = |_| {
(p-assign (ident "outerFunc"))
(e-closure
(captures
(capture (ident "x"))
(capture (ident "y")))
(e-lambda
(args

View file

@ -129,56 +129,59 @@ NO CHANGE
(can-ir
(d-let
(p-assign (ident "processItems"))
(e-lambda
(args
(p-assign (ident "items")))
(e-block
(s-var
(p-assign (ident "count_"))
(e-num (value "0")))
(s-var
(p-assign (ident "total_"))
(e-num (value "0")))
(s-reassign
(p-assign (ident "count_"))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "count_")))
(e-num (value "1"))))
(s-reassign
(p-assign (ident "total_"))
(e-closure
(captures
(capture (ident "nestedFunc")))
(e-lambda
(args
(p-assign (ident "items")))
(e-block
(s-var
(p-assign (ident "count_"))
(e-num (value "0")))
(s-var
(p-assign (ident "total_"))
(e-num (value "0")))
(s-reassign
(p-assign (ident "count_"))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "count_")))
(e-num (value "1"))))
(s-reassign
(p-assign (ident "total_"))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "total_")))
(e-num (value "10"))))
(s-let
(p-assign (ident "nestedFunc"))
(e-closure
(captures
(capture (ident "count_")))
(e-lambda
(args
(p-underscore))
(e-block
(s-reassign
(p-assign (ident "count_"))
(e-runtime-error (tag "var_across_function_boundary")))
(s-reassign
(p-assign (ident "total_"))
(e-runtime-error (tag "var_across_function_boundary")))
(e-lookup-local
(p-assign (ident "count_")))))))
(s-let
(p-assign (ident "result"))
(e-call
(e-lookup-local
(p-assign (ident "nestedFunc")))
(e-empty_record)))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "total_")))
(e-num (value "10"))))
(s-let
(p-assign (ident "nestedFunc"))
(e-closure
(captures
(capture (ident "count_")))
(e-lambda
(args
(p-underscore))
(e-block
(s-reassign
(p-assign (ident "count_"))
(e-runtime-error (tag "var_across_function_boundary")))
(s-reassign
(p-assign (ident "total_"))
(e-runtime-error (tag "var_across_function_boundary")))
(e-lookup-local
(p-assign (ident "count_")))))))
(s-let
(p-assign (ident "result"))
(e-call
(e-lookup-local
(p-assign (ident "nestedFunc")))
(e-empty_record)))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "total_")))
(e-lookup-local
(p-assign (ident "result"))))))))
(p-assign (ident "result")))))))))
~~~
# TYPES
~~~clojure

View file

@ -947,48 +947,51 @@ main = {
(ty-rigid-var-lookup (ty-rigid-var (name "c"))))))))))
(d-let
(p-assign (ident "process_with_method"))
(e-lambda
(args
(p-assign (ident "container"))
(p-assign (ident "value")))
(e-block
(s-let
(p-assign (ident "id"))
(e-lambda
(args
(p-assign (ident "x")))
(e-lookup-local
(p-assign (ident "x")))))
(s-let
(p-assign (ident "_test1"))
(e-call
(e-lookup-local
(p-assign (ident "id")))
(e-num (value "42"))))
(s-let
(p-assign (ident "_test2"))
(e-call
(e-lookup-local
(p-assign (ident "id")))
(e-string
(e-literal (string "test")))))
(s-let
(p-assign (ident "result"))
(e-dot-access (field "map")
(receiver
(e-closure
(captures
(capture (ident "id")))
(e-lambda
(args
(p-assign (ident "container"))
(p-assign (ident "value")))
(e-block
(s-let
(p-assign (ident "id"))
(e-lambda
(args
(p-assign (ident "x")))
(e-lookup-local
(p-assign (ident "container"))))
(args
(e-closure
(captures
(capture (ident "value")))
(e-lambda
(args
(p-underscore))
(e-lookup-local
(p-assign (ident "value"))))))))
(e-lookup-local
(p-assign (ident "result")))))
(p-assign (ident "x")))))
(s-let
(p-assign (ident "_test1"))
(e-call
(e-lookup-local
(p-assign (ident "id")))
(e-num (value "42"))))
(s-let
(p-assign (ident "_test2"))
(e-call
(e-lookup-local
(p-assign (ident "id")))
(e-string
(e-literal (string "test")))))
(s-let
(p-assign (ident "result"))
(e-dot-access (field "map")
(receiver
(e-lookup-local
(p-assign (ident "container"))))
(args
(e-closure
(captures
(capture (ident "value")))
(e-lambda
(args
(p-underscore))
(e-lookup-local
(p-assign (ident "value"))))))))
(e-lookup-local
(p-assign (ident "result"))))))
(annotation
(ty-fn (effectful false)
(ty-rigid-var (name "a"))

View file

@ -1983,29 +1983,32 @@ expect {
(e-num (value "5"))))))
(d-let
(p-assign (ident "add_one"))
(e-lambda
(args
(p-assign (ident "num")))
(e-block
(s-let
(p-assign (ident "other"))
(e-num (value "1")))
(e-if
(if-branches
(if-branch
(e-lookup-local
(p-assign (ident "num")))
(e-closure
(captures
(capture (ident "other")))
(e-lambda
(args
(p-assign (ident "num")))
(e-block
(s-let
(p-assign (ident "other"))
(e-num (value "1")))
(e-if
(if-branches
(if-branch
(e-lookup-local
(p-assign (ident "num")))
(e-block
(s-dbg
(e-call
(e-runtime-error (tag "ident_not_in_scope"))))
(e-num (value "0")))))
(if-else
(e-block
(s-dbg
(e-call
(e-runtime-error (tag "ident_not_in_scope"))))
(e-num (value "0")))))
(if-else
(e-block
(s-dbg
(e-num (value "123")))
(e-lookup-local
(p-assign (ident "other"))))))))
(e-num (value "123")))
(e-lookup-local
(p-assign (ident "other")))))))))
(annotation
(ty-fn (effectful false)
(ty-lookup (name "U64") (builtin))

View file

@ -1755,28 +1755,31 @@ expect {
(e-num (value "5"))))))
(d-let
(p-assign (ident "add_one"))
(e-lambda
(args
(p-assign (ident "num")))
(e-block
(s-let
(p-assign (ident "other"))
(e-num (value "1")))
(e-if
(if-branches
(if-branch
(e-lookup-local
(p-assign (ident "num")))
(e-closure
(captures
(capture (ident "other")))
(e-lambda
(args
(p-assign (ident "num")))
(e-block
(s-let
(p-assign (ident "other"))
(e-num (value "1")))
(e-if
(if-branches
(if-branch
(e-lookup-local
(p-assign (ident "num")))
(e-block
(s-dbg
(e-runtime-error (tag "empty_tuple")))
(e-num (value "0")))))
(if-else
(e-block
(s-dbg
(e-runtime-error (tag "empty_tuple")))
(e-num (value "0")))))
(if-else
(e-block
(s-dbg
(e-num (value "123")))
(e-lookup-local
(p-assign (ident "other"))))))))
(e-num (value "123")))
(e-lookup-local
(p-assign (ident "other")))))))))
(annotation
(ty-fn (effectful false)
(ty-lookup (name "U64") (builtin))

View file

@ -58,29 +58,32 @@ EndOfFile,
# CANONICALIZE
~~~clojure
(e-call
(e-lambda
(args
(p-assign (ident "base")))
(e-block
(s-let
(p-assign (ident "simple"))
(e-closure
(captures
(capture (ident "base")))
(e-lambda
(args
(p-assign (ident "x")))
(e-binop (op "add")
(e-closure
(captures
(capture (ident "simple")))
(e-lambda
(args
(p-assign (ident "base")))
(e-block
(s-let
(p-assign (ident "simple"))
(e-closure
(captures
(capture (ident "base")))
(e-lambda
(args
(p-assign (ident "x")))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "base")))
(e-lookup-local
(p-assign (ident "x"))))
(e-num (value "1"))))))
(e-call
(e-lookup-local
(p-assign (ident "simple")))
(e-num (value "1")))))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "base")))
(e-lookup-local
(p-assign (ident "x"))))
(e-num (value "1"))))))
(e-call
(e-lookup-local
(p-assign (ident "simple")))
(e-num (value "1"))))))
(e-num (value "1")))
~~~
# TYPES

View file

@ -105,15 +105,18 @@ EndOfFile,
(s-let
(p-assign (ident "y"))
(e-call
(e-lambda
(args
(p-underscore))
(e-block
(s-let
(p-assign (ident "x"))
(e-num (value "10")))
(e-lookup-local
(p-assign (ident "x")))))
(e-closure
(captures
(capture (ident "x")))
(e-lambda
(args
(p-underscore))
(e-block
(s-let
(p-assign (ident "x"))
(e-num (value "10")))
(e-lookup-local
(p-assign (ident "x"))))))
(e-empty_record)))
(e-lookup-local
(p-assign (ident "y"))))

View file

@ -82,27 +82,30 @@ NO CHANGE
(can-ir
(d-let
(p-assign (ident "scopedTypeVarInternal"))
(e-lambda
(args
(p-assign (ident "a")))
(e-block
(s-let
(p-assign (ident "b"))
(e-lambda
(args
(p-assign (ident "c")))
(e-block
(s-let
(p-assign (ident "d"))
(e-closure
(captures
(capture (ident "b")))
(e-lambda
(args
(p-assign (ident "a")))
(e-block
(s-let
(p-assign (ident "b"))
(e-lambda
(args
(p-assign (ident "c")))
(e-block
(s-let
(p-assign (ident "d"))
(e-lookup-local
(p-assign (ident "c"))))
(e-lookup-local
(p-assign (ident "c"))))
(e-lookup-local
(p-assign (ident "d"))))))
(e-call
(e-lookup-local
(p-assign (ident "b")))
(e-lookup-local
(p-assign (ident "a"))))))
(p-assign (ident "d"))))))
(e-call
(e-lookup-local
(p-assign (ident "b")))
(e-lookup-local
(p-assign (ident "a")))))))
(annotation
(ty-fn (effectful false)
(ty-rigid-var (name "val"))

View file

@ -1868,29 +1868,32 @@ expect {
(e-num (value "5"))))))
(d-let
(p-assign (ident "add_one"))
(e-lambda
(args
(p-assign (ident "num")))
(e-block
(s-let
(p-assign (ident "other"))
(e-num (value "1")))
(e-if
(if-branches
(if-branch
(e-lookup-local
(p-assign (ident "num")))
(e-closure
(captures
(capture (ident "other")))
(e-lambda
(args
(p-assign (ident "num")))
(e-block
(s-let
(p-assign (ident "other"))
(e-num (value "1")))
(e-if
(if-branches
(if-branch
(e-lookup-local
(p-assign (ident "num")))
(e-block
(s-dbg
(e-call
(e-runtime-error (tag "ident_not_in_scope"))))
(e-num (value "0")))))
(if-else
(e-block
(s-dbg
(e-call
(e-runtime-error (tag "ident_not_in_scope"))))
(e-num (value "0")))))
(if-else
(e-block
(s-dbg
(e-num (value "123")))
(e-lookup-local
(p-assign (ident "other"))))))))
(e-num (value "123")))
(e-lookup-local
(p-assign (ident "other")))))))))
(annotation
(ty-fn (effectful false)
(ty-lookup (name "U64") (builtin))

View file

@ -276,27 +276,30 @@ main! = |_| {
(can-ir
(d-let
(p-assign (ident "main!"))
(e-lambda
(args
(p-underscore))
(e-block
(s-let
(p-assign (ident "userId"))
(e-num (value "123")))
(s-let
(p-assign (ident "person"))
(e-record
(fields
(field (name "name")
(e-string
(e-literal (string "Alice"))))
(field (name "age")
(e-num (value "30"))))))
(s-let
(p-assign (ident "color"))
(e-tag (name "Red")))
(e-lookup-local
(p-assign (ident "userId"))))))
(e-closure
(captures
(capture (ident "userId")))
(e-lambda
(args
(p-underscore))
(e-block
(s-let
(p-assign (ident "userId"))
(e-num (value "123")))
(s-let
(p-assign (ident "person"))
(e-record
(fields
(field (name "name")
(e-string
(e-literal (string "Alice"))))
(field (name "age")
(e-num (value "30"))))))
(s-let
(p-assign (ident "color"))
(e-tag (name "Red")))
(e-lookup-local
(p-assign (ident "userId")))))))
(s-alias-decl
(ty-header (name "UserId"))
(ty-lookup (name "U64") (builtin)))

View file

@ -170,48 +170,54 @@ main! = |_| {}
(can-ir
(d-let
(p-assign (ident "fail"))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "g"))
(e-lambda
(args
(p-assign (ident "z")))
(e-lookup-local
(p-assign (ident "z")))))
(s-let
(p-assign (ident "result"))
(e-call
(e-lookup-local
(p-assign (ident "g")))
(e-lookup-local
(p-assign (ident "x")))))
(e-lookup-local
(p-assign (ident "result")))))
(e-closure
(captures
(capture (ident "g")))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "g"))
(e-lambda
(args
(p-assign (ident "z")))
(e-lookup-local
(p-assign (ident "z")))))
(s-let
(p-assign (ident "result"))
(e-call
(e-lookup-local
(p-assign (ident "g")))
(e-lookup-local
(p-assign (ident "x")))))
(e-lookup-local
(p-assign (ident "result"))))))
(annotation
(ty-fn (effectful false)
(ty-rigid-var (name "a"))
(ty-rigid-var-lookup (ty-rigid-var (name "a"))))))
(d-let
(p-assign (ident "pass"))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "inner"))
(e-lambda
(args
(p-assign (ident "y")))
(e-closure
(captures
(capture (ident "inner")))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "inner"))
(e-lambda
(args
(p-assign (ident "y")))
(e-lookup-local
(p-assign (ident "y")))))
(e-call
(e-lookup-local
(p-assign (ident "y")))))
(e-call
(e-lookup-local
(p-assign (ident "inner")))
(e-lookup-local
(p-assign (ident "x"))))))
(p-assign (ident "inner")))
(e-lookup-local
(p-assign (ident "x")))))))
(annotation
(ty-fn (effectful false)
(ty-rigid-var (name "a"))

View file

@ -102,22 +102,25 @@ main! = |_| {}
(can-ir
(d-let
(p-assign (ident "outer"))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "inner"))
(e-lambda
(args
(p-assign (ident "y")))
(e-closure
(captures
(capture (ident "inner")))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "inner"))
(e-lambda
(args
(p-assign (ident "y")))
(e-lookup-local
(p-assign (ident "y")))))
(e-call
(e-lookup-local
(p-assign (ident "y")))))
(e-call
(e-lookup-local
(p-assign (ident "inner")))
(e-lookup-local
(p-assign (ident "x"))))))
(p-assign (ident "inner")))
(e-lookup-local
(p-assign (ident "x")))))))
(annotation
(ty-fn (effectful false)
(ty-rigid-var (name "a"))

View file

@ -98,22 +98,25 @@ main! = |_| {}
(can-ir
(d-let
(p-assign (ident "outer"))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "inner"))
(e-lambda
(args
(p-assign (ident "y")))
(e-closure
(captures
(capture (ident "inner")))
(e-lambda
(args
(p-assign (ident "x")))
(e-block
(s-let
(p-assign (ident "inner"))
(e-lambda
(args
(p-assign (ident "y")))
(e-lookup-local
(p-assign (ident "y")))))
(e-call
(e-lookup-local
(p-assign (ident "y")))))
(e-call
(e-lookup-local
(p-assign (ident "inner")))
(e-lookup-local
(p-assign (ident "x"))))))
(p-assign (ident "inner")))
(e-lookup-local
(p-assign (ident "x")))))))
(annotation
(ty-fn (effectful false)
(ty-rigid-var (name "a"))

View file

@ -141,31 +141,34 @@ main! = |_| {
(can-ir
(d-let
(p-assign (ident "main!"))
(e-lambda
(args
(p-underscore))
(e-block
(s-let
(p-assign (ident "unused_var"))
(e-num (value "42")))
(s-let
(p-assign (ident "used_var"))
(e-num (value "100")))
(s-let
(p-assign (ident "another_unused"))
(e-string
(e-literal (string "hello"))))
(s-let
(p-assign (ident "_ignored"))
(e-num (value "999")))
(s-let
(p-assign (ident "result"))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "used_var")))
(e-num (value "10"))))
(e-lookup-local
(p-assign (ident "result")))))))
(e-closure
(captures
(capture (ident "used_var")))
(e-lambda
(args
(p-underscore))
(e-block
(s-let
(p-assign (ident "unused_var"))
(e-num (value "42")))
(s-let
(p-assign (ident "used_var"))
(e-num (value "100")))
(s-let
(p-assign (ident "another_unused"))
(e-string
(e-literal (string "hello"))))
(s-let
(p-assign (ident "_ignored"))
(e-num (value "999")))
(s-let
(p-assign (ident "result"))
(e-binop (op "add")
(e-lookup-local
(p-assign (ident "used_var")))
(e-num (value "10"))))
(e-lookup-local
(p-assign (ident "result"))))))))
~~~
# TYPES
~~~clojure