mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Comptime eval updates number literals
This commit is contained in:
parent
819bf6aa8c
commit
d455d73ed4
2 changed files with 197 additions and 0 deletions
|
|
@ -272,9 +272,90 @@ pub const ComptimeEvaluator = struct {
|
|||
const layout_cache = &self.interpreter.runtime_layout_store;
|
||||
defer result.decref(layout_cache, ops);
|
||||
|
||||
// Try to fold the result to a constant expression
|
||||
self.tryFoldConstant(def_idx, result) catch {
|
||||
// If folding fails, just continue - the original expression is still valid
|
||||
};
|
||||
|
||||
return EvalResult.success;
|
||||
}
|
||||
|
||||
/// Attempts to replace an expression with its constant-folded result
|
||||
fn tryFoldConstant(self: *ComptimeEvaluator, def_idx: CIR.Def.Idx, stack_value: eval_mod.StackValue) !void {
|
||||
const def = self.env.store.getDef(def_idx);
|
||||
const expr_idx = def.expr;
|
||||
const region = self.env.store.getExprRegion(expr_idx);
|
||||
|
||||
// Convert StackValue to CIR expression based on layout
|
||||
const layout = stack_value.layout;
|
||||
|
||||
// Check if this is a scalar type (including integers)
|
||||
if (layout.tag != .scalar) {
|
||||
return error.NotImplemented; // Don't fold non-scalar types yet
|
||||
}
|
||||
|
||||
const scalar_tag = layout.data.scalar.tag;
|
||||
const new_expr_idx = switch (scalar_tag) {
|
||||
.int => blk: {
|
||||
// Extract integer value
|
||||
const value = stack_value.asI128();
|
||||
const precision = layout.data.scalar.data.int;
|
||||
|
||||
// Map precision to NumKind
|
||||
const num_kind: CIR.NumKind = switch (precision) {
|
||||
.i8 => .i8,
|
||||
.i16 => .i16,
|
||||
.i32 => .i32,
|
||||
.i64 => .i64,
|
||||
.i128 => .i128,
|
||||
.u8 => .u8,
|
||||
.u16 => .u16,
|
||||
.u32 => .u32,
|
||||
.u64 => .u64,
|
||||
.u128 => .u128,
|
||||
};
|
||||
|
||||
// Create IntValue
|
||||
const int_value = CIR.IntValue{
|
||||
.bytes = @bitCast(value),
|
||||
.kind = switch (precision) {
|
||||
.u8, .u16, .u32, .u64, .u128 => .u128,
|
||||
.i8, .i16, .i32, .i64, .i128 => .i128,
|
||||
},
|
||||
};
|
||||
|
||||
// Create e_num expression
|
||||
const folded_expr = CIR.Expr{
|
||||
.e_num = .{
|
||||
.value = int_value,
|
||||
.kind = num_kind,
|
||||
},
|
||||
};
|
||||
|
||||
// Add the new expression to the store
|
||||
break :blk try self.env.store.addExpr(folded_expr, region);
|
||||
},
|
||||
else => return error.NotImplemented, // Don't fold other scalar types yet
|
||||
};
|
||||
|
||||
// CRITICAL: We need to maintain the type information for the new expression.
|
||||
// We're adding a new CIR node AFTER type checking has completed, so we need
|
||||
// to create a new type variable for it that redirects to the original expression's type.
|
||||
// This ensures the 1-to-1 mapping between CIR nodes and type variables is maintained.
|
||||
const original_type_var = ModuleEnv.varFrom(expr_idx);
|
||||
|
||||
// Create a new type variable that redirects to the original's type
|
||||
// This properly extends the types store
|
||||
_ = try self.env.types.freshRedirect(original_type_var);
|
||||
|
||||
// Replace the expr field in the Def
|
||||
// The Def is stored in extra_data with expr at index 1
|
||||
const nid: CIR.Node.Idx = @enumFromInt(@intFromEnum(def_idx));
|
||||
const node = self.env.store.nodes.get(nid);
|
||||
const extra_start = node.data_1;
|
||||
self.env.store.extra_data.items.items[extra_start + 1] = @intFromEnum(new_expr_idx);
|
||||
}
|
||||
|
||||
/// Helper to report a problem and track allocated message
|
||||
fn reportProblem(
|
||||
self: *ComptimeEvaluator,
|
||||
|
|
|
|||
|
|
@ -604,3 +604,119 @@ test "comptime eval - crash halts within single def" {
|
|||
try testing.expectEqual(@as(u32, 1), summary.crashed);
|
||||
try testing.expectEqual(@as(usize, 1), result.problems.len());
|
||||
}
|
||||
|
||||
// Constant folding tests
|
||||
|
||||
test "comptime eval - constant folding simple addition" {
|
||||
const src = "x = 1 + 1";
|
||||
|
||||
var result = try parseCheckAndEvalModule(src);
|
||||
defer cleanupEvalModule(&result);
|
||||
|
||||
const summary = try result.evaluator.evalAll();
|
||||
|
||||
// Should evaluate successfully
|
||||
try testing.expectEqual(@as(u32, 1), summary.evaluated);
|
||||
try testing.expectEqual(@as(u32, 0), summary.crashed);
|
||||
|
||||
// Verify the expression was folded to a constant
|
||||
const defs = result.module_env.store.sliceDefs(result.module_env.all_defs);
|
||||
try testing.expectEqual(@as(usize, 1), defs.len);
|
||||
|
||||
const def = result.module_env.store.getDef(defs[0]);
|
||||
const expr = result.module_env.store.getExpr(def.expr);
|
||||
|
||||
// The expression should now be e_num with value 2
|
||||
try testing.expect(expr == .e_num);
|
||||
const value = expr.e_num.value.toI128();
|
||||
try testing.expectEqual(@as(i128, 2), value);
|
||||
}
|
||||
|
||||
test "comptime eval - constant folding multiplication" {
|
||||
const src = "x = 21 * 2";
|
||||
|
||||
var result = try parseCheckAndEvalModule(src);
|
||||
defer cleanupEvalModule(&result);
|
||||
|
||||
const summary = try result.evaluator.evalAll();
|
||||
|
||||
try testing.expectEqual(@as(u32, 1), summary.evaluated);
|
||||
try testing.expectEqual(@as(u32, 0), summary.crashed);
|
||||
|
||||
// Verify the expression was folded
|
||||
const defs = result.module_env.store.sliceDefs(result.module_env.all_defs);
|
||||
const def = result.module_env.store.getDef(defs[0]);
|
||||
const expr = result.module_env.store.getExpr(def.expr);
|
||||
|
||||
try testing.expect(expr == .e_num);
|
||||
const value = expr.e_num.value.toI128();
|
||||
try testing.expectEqual(@as(i128, 42), value);
|
||||
}
|
||||
|
||||
test "comptime eval - constant folding preserves literal" {
|
||||
const src = "x = 42";
|
||||
|
||||
var result = try parseCheckAndEvalModule(src);
|
||||
defer cleanupEvalModule(&result);
|
||||
|
||||
const summary = try result.evaluator.evalAll();
|
||||
|
||||
try testing.expectEqual(@as(u32, 1), summary.evaluated);
|
||||
try testing.expectEqual(@as(u32, 0), summary.crashed);
|
||||
|
||||
// The expression should stay as e_num with value 42
|
||||
const defs = result.module_env.store.sliceDefs(result.module_env.all_defs);
|
||||
const def = result.module_env.store.getDef(defs[0]);
|
||||
const expr = result.module_env.store.getExpr(def.expr);
|
||||
|
||||
try testing.expect(expr == .e_num);
|
||||
const value = expr.e_num.value.toI128();
|
||||
try testing.expectEqual(@as(i128, 42), value);
|
||||
}
|
||||
|
||||
test "comptime eval - constant folding multiple defs" {
|
||||
const src =
|
||||
\\a = 10 + 5
|
||||
\\b = 20 * 2
|
||||
\\c = 100 - 58
|
||||
;
|
||||
|
||||
var result = try parseCheckAndEvalModule(src);
|
||||
defer cleanupEvalModule(&result);
|
||||
|
||||
const summary = try result.evaluator.evalAll();
|
||||
|
||||
try testing.expectEqual(@as(u32, 3), summary.evaluated);
|
||||
try testing.expectEqual(@as(u32, 0), summary.crashed);
|
||||
|
||||
// Verify all expressions were folded
|
||||
const defs = result.module_env.store.sliceDefs(result.module_env.all_defs);
|
||||
try testing.expectEqual(@as(usize, 3), defs.len);
|
||||
|
||||
// Check a = 15
|
||||
{
|
||||
const def = result.module_env.store.getDef(defs[0]);
|
||||
const expr = result.module_env.store.getExpr(def.expr);
|
||||
try testing.expect(expr == .e_num);
|
||||
const value = expr.e_num.value.toI128();
|
||||
try testing.expectEqual(@as(i128, 15), value);
|
||||
}
|
||||
|
||||
// Check b = 40
|
||||
{
|
||||
const def = result.module_env.store.getDef(defs[1]);
|
||||
const expr = result.module_env.store.getExpr(def.expr);
|
||||
try testing.expect(expr == .e_num);
|
||||
const value = expr.e_num.value.toI128();
|
||||
try testing.expectEqual(@as(i128, 40), value);
|
||||
}
|
||||
|
||||
// Check c = 42
|
||||
{
|
||||
const def = result.module_env.store.getDef(defs[2]);
|
||||
const expr = result.module_env.store.getExpr(def.expr);
|
||||
try testing.expect(expr == .e_num);
|
||||
const value = expr.e_num.value.toI128();
|
||||
try testing.expectEqual(@as(i128, 42), value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue