Comptime eval updates number literals

This commit is contained in:
Richard Feldman 2025-10-18 22:17:36 -04:00
parent 819bf6aa8c
commit d455d73ed4
No known key found for this signature in database
2 changed files with 197 additions and 0 deletions

View file

@ -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,

View file

@ -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);
}
}