lang: rename signed integer types sN -> iN
Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
This commit is contained in:
@@ -29,7 +29,7 @@ const hasComptimeParams = Lowering.hasComptimeParams;
|
||||
pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
var c = c_in;
|
||||
// A bare reserved-type-name spelling in call position parses as a
|
||||
// `.type_expr` (e.g. `s2(4)`), but if a function of that name is in
|
||||
// `.type_expr` (e.g. `i2(4)`), but if a function of that name is in
|
||||
// scope — a backtick-declared sx fn or a `#import c` foreign fn whose C
|
||||
// name collides with a reserved type spelling — it is a CALL to that
|
||||
// function. `TypeName(val)` is not a cast (casts are `cast(T, val)`), so
|
||||
@@ -411,7 +411,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// Check builtins first (these are handled natively by interpreter and emitter)
|
||||
if (resolveBuiltin(id.name)) |bid| {
|
||||
const ret_ty: TypeId = switch (bid) {
|
||||
.size_of, .align_of => .s64,
|
||||
.size_of, .align_of => .i64,
|
||||
.sqrt, .sin, .cos, .floor => blk: {
|
||||
// Math builtins: return type matches argument type ($T -> T)
|
||||
if (c.args.len > 0) {
|
||||
@@ -530,8 +530,8 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
const callee_ref = if (binding.is_alloca) self.builder.load(binding.ref, binding.ty) else binding.ref;
|
||||
const ret_ty = if (!binding.ty.isBuiltin()) blk: {
|
||||
const bti = self.module.types.get(binding.ty);
|
||||
break :blk if (bti == .function) bti.function.ret else .s64;
|
||||
} else .s64;
|
||||
break :blk if (bti == .function) bti.function.ret else .i64;
|
||||
} else .i64;
|
||||
var final_args = std.ArrayList(Ref).empty;
|
||||
defer final_args.deinit(self.alloc);
|
||||
if (self.fnPtrTypeWantsCtx(binding.ty)) {
|
||||
@@ -623,8 +623,8 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// Type constructor call: Sx(f32).user(0.5) — obj is a call that returns a type
|
||||
if (fa.object.data == .call) {
|
||||
const inner_call = &fa.object.data.call;
|
||||
// Generic struct STATIC-METHOD head (`Box(s64).make(..)` or the
|
||||
// qualified `a.Box(s64).make(..)`): the layout author is chosen
|
||||
// Generic struct STATIC-METHOD head (`Box(i64).make(..)` or the
|
||||
// qualified `a.Box(i64).make(..)`): the layout author is chosen
|
||||
// by the single head choke-point (CP-1) and the method body by
|
||||
// the instance's STAMPED author (CP-4), so layout-author ≡
|
||||
// body-author for BOTH bare and qualified heads (E4 #1 / #2).
|
||||
@@ -1025,7 +1025,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// call PLAN selected for the receiver's source — the SAME author
|
||||
// plan typed the call's result as, so dispatch and typing can't
|
||||
// disagree (without this, a string-typed winner over
|
||||
// an s64 shadow boxes a raw int as a string pointer → segfault).
|
||||
// an i64 shadow boxes a raw int as a string pointer → segfault).
|
||||
// The plan is the single producer; lowering consumes its verdict
|
||||
// (`sel_author` / `cplan.ambiguous_collision`, computed once above)
|
||||
// rather than re-resolving the field name. `.ambiguous` → loud
|
||||
@@ -1189,7 +1189,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// Indirect call through expression
|
||||
const callee_ref = self.lowerExpr(c.callee);
|
||||
const owned = self.alloc.dupe(Ref, args.items) catch unreachable;
|
||||
return self.builder.emit(.{ .call_indirect = .{ .callee = callee_ref, .args = owned } }, .s64);
|
||||
return self.builder.emit(.{ .call_indirect = .{ .callee = callee_ref, .args = owned } }, .i64);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1426,7 +1426,7 @@ pub fn lowerRuntimeDispatchCall(
|
||||
const type_tag_raw = self.lowerExpr(type_tag_node orelse return self.emitError("dispatch", call_node.callee.span));
|
||||
const type_tag_node_ty = self.inferExprType(type_tag_node.?);
|
||||
const type_tag = if (type_tag_node_ty == .any)
|
||||
self.builder.emit(.{ .unbox_any = .{ .operand = type_tag_raw } }, .s64)
|
||||
self.builder.emit(.{ .unbox_any = .{ .operand = type_tag_raw } }, .i64)
|
||||
else
|
||||
type_tag_raw;
|
||||
const any_val = self.lowerExpr(any_val_node orelse return self.emitError("dispatch", call_node.callee.span));
|
||||
@@ -1630,13 +1630,13 @@ pub fn lowerRuntimeDispatchCall(
|
||||
@memcpy(final_call_args[1..], call_args.items);
|
||||
}
|
||||
}
|
||||
// Coerce non-cast args (source type unknown, use s64 default).
|
||||
// Coerce non-cast args (source type unknown, use i64 default).
|
||||
// cast_arg_idx is in user-space (skips __sx_ctx); offset by ctx_slots.
|
||||
const ctx_slots: usize = if (callee_has_ctx) 1 else 0;
|
||||
for (0..@min(final_call_args.len, callee_params.len)) |ci| {
|
||||
if (ci < ctx_slots) continue; // skip __sx_ctx slot
|
||||
if ((ci - ctx_slots) != cast_arg_idx) {
|
||||
final_call_args[ci] = self.coerceToType(final_call_args[ci], .s64, callee_params[ci].ty);
|
||||
final_call_args[ci] = self.coerceToType(final_call_args[ci], .i64, callee_params[ci].ty);
|
||||
}
|
||||
}
|
||||
const result = self.builder.call(fid, final_call_args, callee_ret);
|
||||
@@ -1680,12 +1680,12 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// size_of(T) → const_int(sizeof(T))
|
||||
const ty = self.resolveTypeArg(c.args[0]);
|
||||
const size: i64 = @intCast(self.typeSizeBytes(ty));
|
||||
return self.builder.constInt(size, .s64);
|
||||
return self.builder.constInt(size, .i64);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "align_of")) {
|
||||
const ty = self.resolveTypeArg(c.args[0]);
|
||||
const a: i64 = @intCast(self.module.types.typeAlignBytes(ty));
|
||||
return self.builder.constInt(a, .s64);
|
||||
return self.builder.constInt(a, .i64);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "field_count")) {
|
||||
// field_count(T) → const_int(N)
|
||||
@@ -1700,7 +1700,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
.vector => |v| @intCast(v.length),
|
||||
else => 0,
|
||||
};
|
||||
return self.builder.constInt(count, .s64);
|
||||
return self.builder.constInt(count, .i64);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_name")) {
|
||||
// type_name(T):
|
||||
@@ -1712,8 +1712,8 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// `callBuiltin(.type_name, [arg_ref])`. The interp's
|
||||
// arm (commit 9600ba5) reads the runtime `.type_tag`
|
||||
// and returns the per-position name. Without this
|
||||
// split, the catch-all `else => .s64` in
|
||||
// `resolveTypeArg` silently returns "s64" for every
|
||||
// split, the catch-all `else => .i64` in
|
||||
// `resolveTypeArg` silently returns "i64" for every
|
||||
// dynamic call — exactly the silent-arm pattern the
|
||||
// project's REJECTED PATTERNS forbid.
|
||||
if (self.isStaticTypeArg(c.args[0])) {
|
||||
@@ -1729,8 +1729,8 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
if (std.mem.eql(u8, name, "type_eq")) {
|
||||
// type_eq(T1, T2) → const_bool — comptime TypeId equality.
|
||||
// TypeIds are interned per structural shape so equality on
|
||||
// them matches the user's intuition: `type_eq(s64, s64)` is
|
||||
// true, `type_eq(*s64, *s64)` is true, distinct shapes are
|
||||
// them matches the user's intuition: `type_eq(i64, i64)` is
|
||||
// true, `type_eq(*i64, *i64)` is true, distinct shapes are
|
||||
// false. Pack-indexed types (`$args[0]`) resolve through
|
||||
// `resolveTypeArg` → `resolveTypeWithBindings`.
|
||||
if (c.args.len < 2) return self.builder.constBool(false);
|
||||
@@ -1745,7 +1745,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// `any_to_string` — emits a `callBuiltin`: the interp reads
|
||||
// the boxed TypeId, LLVM GEPs a per-type signedness table.
|
||||
// Mirrors `type_name`'s static/dynamic split; the same split
|
||||
// avoids `resolveTypeArg`'s silent `.s64` default lying about
|
||||
// avoids `resolveTypeArg`'s silent `.i64` default lying about
|
||||
// a runtime Type value.
|
||||
if (c.args.len < 1) return self.builder.constBool(false);
|
||||
if (self.isStaticTypeArg(c.args[0])) {
|
||||
@@ -1873,7 +1873,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// the returned value carries Type semantics (tag field
|
||||
// says ".any" → the value field holds the type id).
|
||||
const val = self.lowerExpr(c.args[0]);
|
||||
const tag_val = self.builder.structGet(val, 0, .s64);
|
||||
const tag_val = self.builder.structGet(val, 0, .i64);
|
||||
return self.builder.boxAny(tag_val, .any);
|
||||
} else {
|
||||
return self.builder.constType(arg_ty);
|
||||
@@ -1881,14 +1881,14 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
}
|
||||
if (std.mem.eql(u8, name, "field_index")) {
|
||||
// field_index(T, val) → extract tag from tagged union
|
||||
if (c.args.len < 2) return self.builder.constInt(0, .s64);
|
||||
if (c.args.len < 2) return self.builder.constInt(0, .i64);
|
||||
const val = self.lowerExpr(c.args[1]);
|
||||
// For tagged unions: extract field 0 (the tag)
|
||||
return self.builder.emit(.{ .enum_tag = .{ .operand = val } }, .s64);
|
||||
return self.builder.emit(.{ .enum_tag = .{ .operand = val } }, .i64);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "field_value_int")) {
|
||||
// field_value_int(T, i) → lookup enum variant value by index
|
||||
if (c.args.len < 2) return self.builder.constInt(0, .s64);
|
||||
if (c.args.len < 2) return self.builder.constInt(0, .i64);
|
||||
const ty = self.resolveTypeArg(c.args[0]);
|
||||
const idx = self.lowerExpr(c.args[1]);
|
||||
// For enums with explicit values, build a global value array and index into it
|
||||
@@ -1901,11 +1901,11 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
var elems = std.ArrayList(Ref).empty;
|
||||
defer elems.deinit(self.alloc);
|
||||
for (vals) |v| {
|
||||
elems.append(self.alloc, self.builder.constInt(v, .s64)) catch unreachable;
|
||||
elems.append(self.alloc, self.builder.constInt(v, .i64)) catch unreachable;
|
||||
}
|
||||
const arr_ty = self.module.types.arrayOf(.s64, @intCast(vals.len));
|
||||
const arr_ty = self.module.types.arrayOf(.i64, @intCast(vals.len));
|
||||
const arr = self.builder.structInit(elems.items, arr_ty);
|
||||
return self.builder.emit(.{ .index_get = .{ .lhs = arr, .rhs = idx } }, .s64);
|
||||
return self.builder.emit(.{ .index_get = .{ .lhs = arr, .rhs = idx } }, .i64);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1921,7 +1921,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
/// shapes), or a runtime `Type` value — which is `.any`-typed at
|
||||
/// runtime (`type_of(x)`, a `[]Type` element `list[i]`, a `Type`-typed
|
||||
/// local / field / param). Any other expression — a value of type
|
||||
/// s64 / f64 / bool / a struct — is NOT a type.
|
||||
/// i64 / f64 / bool / a struct — is NOT a type.
|
||||
pub fn reflectionArgIsType(self: *Lowering, arg: *const Node) bool {
|
||||
if (self.isStaticTypeArg(arg)) return true;
|
||||
return self.inferExprType(arg) == .any;
|
||||
@@ -1986,7 +1986,7 @@ pub fn reflectionErrorSentinel(self: *Lowering, name: []const u8) Ref {
|
||||
std.mem.eql(u8, name, "type_is_unsigned") or
|
||||
std.mem.eql(u8, name, "is_flags"))
|
||||
return self.builder.constBool(false);
|
||||
return self.builder.constInt(0, .s64);
|
||||
return self.builder.constInt(0, .i64);
|
||||
}
|
||||
|
||||
/// After args have been lowered, append the lowered values of any
|
||||
|
||||
@@ -214,7 +214,7 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
||||
const env_local = self.builder.alloca(env_struct_ty);
|
||||
// Compute env size
|
||||
const env_byte_size_inner = self.computeEnvSize(capture_list);
|
||||
const env_size_val = self.builder.constInt(@intCast(env_byte_size_inner), .s64);
|
||||
const env_size_val = self.builder.constInt(@intCast(env_byte_size_inner), .i64);
|
||||
// memcpy(local_alloca, env_param, size)
|
||||
_ = self.callForeign("memcpy", &.{ env_local, env_param_ref, env_size_val }, self.module.types.ptrTo(.void));
|
||||
|
||||
@@ -247,7 +247,7 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
||||
// Bind params (user args start at user_param_base_lam, shifted past ctx + env).
|
||||
// Use the signature types computed above (`params`), which already
|
||||
// applied contextual typing from the target closure to untyped params —
|
||||
// `resolveParamType` alone would drop it and default each to s64.
|
||||
// `resolveParamType` alone would drop it and default each to i64.
|
||||
for (lam.params, 0..) |p, i| {
|
||||
const pty = params.items[user_param_base + i].ty;
|
||||
const slot = self.builder.alloca(pty);
|
||||
@@ -355,7 +355,7 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
||||
// `push Context.{ allocator = ... }` and a tracker / arena
|
||||
// counts the env allocation alongside everything else.
|
||||
const env_byte_size = self.computeEnvSize(capture_list);
|
||||
const env_size = self.builder.constInt(@intCast(env_byte_size), .s64);
|
||||
const env_size = self.builder.constInt(@intCast(env_byte_size), .i64);
|
||||
const ptr_void = self.module.types.ptrTo(.void);
|
||||
const env_heap = self.allocViaContext(env_size, ptr_void);
|
||||
// memcpy(heap, stack_alloca, size)
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn lowerXX(self: *Lowering, operand: Ref, operand_node: *const Node) Ref {
|
||||
// Use the operand's *actual* lowered Ref type rather than reaching
|
||||
// back through inferExprType — the latter doesn't cover every
|
||||
// expression shape (notably lambdas), and a wrong src_ty here can
|
||||
// route the cast through coerceToType (e.g. a bogus s64→ptr bitcast)
|
||||
// route the cast through coerceToType (e.g. a bogus i64→ptr bitcast)
|
||||
// and silently skip the user-space Into fallback.
|
||||
const src_ty = self.builder.getRefType(operand);
|
||||
const target_explicit = self.target_type != null;
|
||||
@@ -83,7 +83,7 @@ pub fn lowerXX(self: *Lowering, operand: Ref, operand_node: *const Node) Ref {
|
||||
const result = self.coerceExplicit(operand, src_ty, dst_ty);
|
||||
|
||||
// User-space fallback via `impl Into(Target) for Source`. Only fires
|
||||
// when the target was explicitly named (not the .s64 default), src and
|
||||
// when the target was explicitly named (not the .i64 default), src and
|
||||
// dst differ, and the built-in ladder made no progress. Built-ins
|
||||
// always win.
|
||||
if (target_explicit and src_ty != dst_ty and result == operand) {
|
||||
@@ -445,14 +445,14 @@ pub fn lowerAnyToF64Dispatch(self: *Lowering, any_val: Ref) Ref {
|
||||
const result_slot = self.builder.alloca(.f64);
|
||||
|
||||
// Extract type tag from Any
|
||||
const tag = self.builder.structGet(any_val, 0, .s64);
|
||||
const tag = self.builder.structGet(any_val, 0, .i64);
|
||||
|
||||
const f32_bb = self.freshBlock("f32.unbox");
|
||||
const f64_bb = self.freshBlock("f64.unbox");
|
||||
const merge_bb = self.freshBlock("float.merge");
|
||||
|
||||
// Branch: tag == f32_tag ? f32_bb : f64_bb
|
||||
const f32_tag = self.builder.constInt(TypeId.f32.index(), .s64);
|
||||
const f32_tag = self.builder.constInt(TypeId.f32.index(), .i64);
|
||||
const cond = self.builder.emit(.{ .cmp_eq = .{ .lhs = tag, .rhs = f32_tag } }, .bool);
|
||||
self.builder.condBr(cond, f32_bb, &.{}, f64_bb, &.{});
|
||||
|
||||
@@ -479,7 +479,7 @@ pub fn lowerAnyToF64Dispatch(self: *Lowering, any_val: Ref) Ref {
|
||||
}
|
||||
|
||||
/// Produce a default value for a type, applying struct field defaults.
|
||||
/// For structs with defaults (e.g., `b: s32 = 99`), creates a struct_literal with defaults applied.
|
||||
/// For structs with defaults (e.g., `b: i32 = 99`), creates a struct_literal with defaults applied.
|
||||
/// For other types, returns a zero value.
|
||||
pub fn buildDefaultValue(self: *Lowering, ty: TypeId) Ref {
|
||||
if (ty.isBuiltin()) return self.builder.constInt(0, ty);
|
||||
@@ -534,7 +534,7 @@ pub fn zeroValue(self: *Lowering, ty: TypeId) Ref {
|
||||
if (ty.isBuiltin()) return self.builder.constInt(0, ty);
|
||||
const info = self.module.types.get(ty);
|
||||
return switch (info) {
|
||||
// Arbitrary-width integer types (u1, u2, s4, ...) interned as
|
||||
// Arbitrary-width integer types (u1, u2, i4, ...) interned as
|
||||
// `.signed`/`.unsigned` variants — fall through `isBuiltin()`.
|
||||
.signed, .unsigned => self.builder.constInt(0, ty),
|
||||
.pointer, .tuple, .optional => self.builder.constNull(ty),
|
||||
@@ -610,8 +610,8 @@ pub fn coerceMode(self: *Lowering, val: Ref, src_ty: TypeId, dst_ty: TypeId, mod
|
||||
}
|
||||
return val;
|
||||
},
|
||||
// Tuple → Tuple element-wise coercion (e.g. a `(s64, s64)` literal
|
||||
// flowing into a `(s32, s32)` slot — the multi-value failable success
|
||||
// Tuple → Tuple element-wise coercion (e.g. a `(i64, i64)` literal
|
||||
// flowing into a `(i32, i32)` slot — the multi-value failable success
|
||||
// tuple). Same arity: extract each slot, coerce it, rebuild.
|
||||
.tuple_elementwise => {
|
||||
const si = self.module.types.get(src_ty);
|
||||
@@ -693,14 +693,14 @@ pub fn coerceMode(self: *Lowering, val: Ref, src_ty: TypeId, dst_ty: TypeId, mod
|
||||
}
|
||||
|
||||
/// Apply C default argument promotion to variadic-tail args. These rules
|
||||
/// (bool/s8/s16/u8/u16 → s32, f32 → f64) match the C calling convention's
|
||||
/// (bool/i8/i16/u8/u16 → i32, f32 → f64) match the C calling convention's
|
||||
/// implicit promotions when an argument is passed through `...`.
|
||||
pub fn promoteCVariadicArgs(self: *Lowering, args: []Ref, fixed_count: usize) void {
|
||||
if (args.len <= fixed_count) return;
|
||||
for (args[fixed_count..]) |*arg| {
|
||||
const src_ty = self.builder.getRefType(arg.*);
|
||||
const promoted: TypeId = switch (src_ty) {
|
||||
.bool, .s8, .s16, .u8, .u16 => .s32,
|
||||
.bool, .i8, .i16, .u8, .u16 => .i32,
|
||||
.f32 => .f64,
|
||||
else => continue,
|
||||
};
|
||||
@@ -720,7 +720,7 @@ pub fn coerceCallArgs(self: *Lowering, args: []Ref, params: []const Function.Par
|
||||
if (src_info == .array and dst_info == .many_pointer) {
|
||||
const slot = self.builder.alloca(src_ty);
|
||||
self.builder.store(slot, args[i]);
|
||||
const zero = self.builder.constInt(0, .s64);
|
||||
const zero = self.builder.constInt(0, .i64);
|
||||
args[i] = self.builder.emit(.{ .index_gep = .{ .lhs = slot, .rhs = zero } }, dst_ty);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ pub fn evalComptimeInt(self: *Lowering, node: *const Node) ?i64 {
|
||||
pub fn lowerComptimeGlobal(self: *Lowering, name: []const u8, expr: *const Node, type_ann: ?*const Node) void {
|
||||
// When the user writes `NAME :: #run expr;` with no type annotation,
|
||||
// infer the global's type from the comptime expression's return
|
||||
// shape. `resolveType(null)` returns `.s64` for legacy reasons —
|
||||
// shape. `resolveType(null)` returns `.i64` for legacy reasons —
|
||||
// good for primitive helpers, silently wrong for anything else.
|
||||
const expr_ty = self.inferExprType(expr);
|
||||
// A failable `#run` (bare, no `catch`/`or`): the comptime function
|
||||
@@ -1130,7 +1130,7 @@ pub fn foldComptimeFloatInit(self: *Lowering, node: *const Node, dst: TypeId) ?R
|
||||
// `evalConstFloatExpr` only succeeds for literal / const-arithmetic
|
||||
// nodes, never an unbound pack index. `inferExprType` is the primary
|
||||
// signal, but it reads a const's DECLARED type — which is a placeholder
|
||||
// `s64` for an untyped float-EXPRESSION const (`ME :: 4.0 + 1.0`), so
|
||||
// `i64` for an untyped float-EXPRESSION const (`ME :: 4.0 + 1.0`), so
|
||||
// `ME / 2` would look like integer division; `isFloatValuedExpr` (judging
|
||||
// by VALUE) catches that case so it narrows under the unified rule too.
|
||||
if (!isFloat(self.inferExprType(node)) and !program_index_mod.isFloatValuedExpr(node, self)) return null;
|
||||
|
||||
@@ -61,7 +61,7 @@ pub fn lowerIfExpr(self: *Lowering, ie: *const ast.IfExpr) Ref {
|
||||
|
||||
// Optional binding: `if val := expr { ... }`
|
||||
// Clear target_type so the ternary's result type doesn't leak into the condition
|
||||
// (e.g., `if x != 0 then 1.0 else 2.0` — the `0` must be s64, not f32)
|
||||
// (e.g., `if x != 0 then 1.0 else 2.0` — the `0` must be i64, not f32)
|
||||
const saved_cond_target = self.target_type;
|
||||
self.target_type = null;
|
||||
const opt_val = self.lowerExpr(ie.condition);
|
||||
@@ -273,12 +273,12 @@ pub fn listView(self: *Lowering, value: Ref, ty: TypeId) ?struct { data: Ref, da
|
||||
return .{
|
||||
.data = self.builder.emit(.{ .struct_get = .{ .base = value, .field_index = items_idx.? } }, items_ty),
|
||||
.data_ty = items_ty,
|
||||
.len = self.builder.emit(.{ .struct_get = .{ .base = value, .field_index = len_idx.? } }, .s64),
|
||||
.len = self.builder.emit(.{ .struct_get = .{ .base = value, .field_index = len_idx.? } }, .i64),
|
||||
};
|
||||
}
|
||||
|
||||
/// Lowered prep for one position of a multi-iterable `for` header. Every
|
||||
/// position gets its own s64 cursor slot (ranges start at their `start`,
|
||||
/// position gets its own i64 cursor slot (ranges start at their `start`,
|
||||
/// collections at 0); all cursors advance by 1 per iteration, and ONLY the
|
||||
/// first position's bound terminates the loop (first-iterable-wins).
|
||||
const IterPrep = struct {
|
||||
@@ -316,13 +316,13 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
for (fe.iterables, 0..) |it, i| {
|
||||
if (it.is_range) {
|
||||
var start_ref = self.lowerExpr(it.expr);
|
||||
if (it.start_exclusive) start_ref = self.builder.add(start_ref, self.builder.constInt(1, .s64), .s64);
|
||||
const slot = self.builder.alloca(.s64);
|
||||
if (it.start_exclusive) start_ref = self.builder.add(start_ref, self.builder.constInt(1, .i64), .i64);
|
||||
const slot = self.builder.alloca(.i64);
|
||||
self.builder.store(slot, start_ref);
|
||||
if (i == 0) {
|
||||
// Parser guarantees the first iterable is bounded.
|
||||
var end_ref = self.lowerExpr(it.range_end.?);
|
||||
if (it.end_inclusive) end_ref = self.builder.add(end_ref, self.builder.constInt(1, .s64), .s64);
|
||||
if (it.end_inclusive) end_ref = self.builder.add(end_ref, self.builder.constInt(1, .i64), .i64);
|
||||
limit = end_ref;
|
||||
}
|
||||
preps.append(self.alloc, .{ .is_range = true, .slot = slot }) catch unreachable;
|
||||
@@ -349,7 +349,7 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
data_ty = lv.data_ty;
|
||||
len = lv.len;
|
||||
} else if (i == 0) {
|
||||
len = self.builder.emit(.{ .length = .{ .operand = data } }, .s64);
|
||||
len = self.builder.emit(.{ .length = .{ .operand = data } }, .i64);
|
||||
}
|
||||
|
||||
const elem_ty = self.getElementType(data_ty);
|
||||
@@ -367,8 +367,8 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
}
|
||||
const is_array = !data_ty.isBuiltin() and self.module.types.get(data_ty) == .array;
|
||||
const storage = if (is_array and !was_deref) self.getExprAlloca(it.expr) else null;
|
||||
const slot = self.builder.alloca(.s64);
|
||||
self.builder.store(slot, self.builder.constInt(0, .s64));
|
||||
const slot = self.builder.alloca(.i64);
|
||||
self.builder.store(slot, self.builder.constInt(0, .i64));
|
||||
if (i == 0) limit = len;
|
||||
preps.append(self.alloc, .{
|
||||
.is_range = false,
|
||||
@@ -391,7 +391,7 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
|
||||
// Header: first cursor against the first bound.
|
||||
self.builder.switchToBlock(header_bb);
|
||||
const cur0 = self.builder.load(preps.items[0].slot, .s64);
|
||||
const cur0 = self.builder.load(preps.items[0].slot, .i64);
|
||||
const cmp = self.builder.cmpLt(cur0, limit);
|
||||
self.builder.condBr(cmp, body_bb, &.{}, exit_bb, &.{});
|
||||
|
||||
@@ -404,9 +404,9 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
|
||||
for (fe.captures, 0..) |cap, i| {
|
||||
const prep = preps.items[i];
|
||||
const cur = if (i == 0) cur0 else self.builder.load(prep.slot, .s64);
|
||||
const cur = if (i == 0) cur0 else self.builder.load(prep.slot, .i64);
|
||||
if (prep.is_range) {
|
||||
body_scope.put(cap.name, .{ .ref = cur, .ty = .s64, .is_alloca = false });
|
||||
body_scope.put(cap.name, .{ .ref = cur, .ty = .i64, .is_alloca = false });
|
||||
continue;
|
||||
}
|
||||
const bind_ty = if (cap.by_ref) self.module.types.ptrTo(prep.elem_ty) else prep.elem_ty;
|
||||
@@ -453,10 +453,10 @@ pub fn lowerFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
// Increment block: advance every cursor and jump back to header.
|
||||
self.builder.switchToBlock(inc_bb);
|
||||
{
|
||||
const one = self.builder.constInt(1, .s64);
|
||||
const one = self.builder.constInt(1, .i64);
|
||||
for (preps.items) |prep| {
|
||||
const cur = self.builder.load(prep.slot, .s64);
|
||||
const next = self.builder.add(cur, one, .s64);
|
||||
const cur = self.builder.load(prep.slot, .i64);
|
||||
const next = self.builder.add(cur, one, .i64);
|
||||
self.builder.store(prep.slot, next);
|
||||
}
|
||||
self.builder.br(header_bb, &.{});
|
||||
@@ -556,7 +556,7 @@ pub fn lowerInlineRangeFor(self: *Lowering, fe: *const ast.ForExpr) Ref {
|
||||
// uses like `print(i)`) and as a comptime constant (for
|
||||
// `xs[i]` substitution).
|
||||
const v = start + i;
|
||||
body_scope.put(cap.name, .{ .ref = self.builder.constInt(v, .s64), .ty = .s64, .is_alloca = false });
|
||||
body_scope.put(cap.name, .{ .ref = self.builder.constInt(v, .i64), .ty = .i64, .is_alloca = false });
|
||||
var save = CursorSave{ .name = cap.name, .had_prev = false, .prev = undefined };
|
||||
if (self.comptime_constants.get(cap.name)) |p| {
|
||||
save.had_prev = true;
|
||||
@@ -796,11 +796,11 @@ pub fn lowerMatch(self: *Lowering, me: *const ast.MatchExpr) Ref {
|
||||
}
|
||||
|
||||
// Switch on the subject (for type match, subject is either a
|
||||
// bare TypeId (s64) or an Any-shaped Type value — unbox in the
|
||||
// bare TypeId (i64) or an Any-shaped Type value — unbox in the
|
||||
// latter case so the switch sees the i64 type id).
|
||||
const tag = if (is_type_match) tag_blk: {
|
||||
if (subject_ty == .any) {
|
||||
break :tag_blk self.builder.emit(.{ .unbox_any = .{ .operand = subject } }, .s64);
|
||||
break :tag_blk self.builder.emit(.{ .unbox_any = .{ .operand = subject } }, .i64);
|
||||
}
|
||||
break :tag_blk subject;
|
||||
} else if (is_optional_match) self.builder.emit(.{ .optional_has_value = .{ .operand = subject } }, .bool) else if (is_error_set_match) subject else blk: {
|
||||
@@ -810,7 +810,7 @@ pub fn lowerMatch(self: *Lowering, me: *const ast.MatchExpr) Ref {
|
||||
const ty_info = self.module.types.get(subject_ty);
|
||||
if (ty_info == .tagged_union) break :tt ty_info.tagged_union.tag_type;
|
||||
}
|
||||
break :tt .s32;
|
||||
break :tt .i32;
|
||||
};
|
||||
break :blk self.builder.enumTag(subject, tag_ty);
|
||||
};
|
||||
@@ -836,7 +836,7 @@ pub fn lowerMatch(self: *Lowering, me: *const ast.MatchExpr) Ref {
|
||||
if (is_optional_match) {
|
||||
// For optional match, unwrap the optional value
|
||||
const opt_info = self.module.types.get(subject_ty);
|
||||
const child_ty = if (opt_info == .optional) opt_info.optional.child else .s64;
|
||||
const child_ty = if (opt_info == .optional) opt_info.optional.child else .i64;
|
||||
const unwrapped = self.builder.emit(.{ .optional_unwrap = .{ .operand = subject } }, child_ty);
|
||||
arm_scope.put(capture_name, .{ .ref = unwrapped, .ty = child_ty, .is_alloca = false });
|
||||
} else {
|
||||
|
||||
@@ -268,7 +268,7 @@ pub fn injectComptimeConstants(self: *Lowering) void {
|
||||
}
|
||||
}
|
||||
|
||||
// POINTER_SIZE: s64 (4 for wasm32, 8 for wasm64 and other 64-bit targets)
|
||||
// POINTER_SIZE: i64 (4 for wasm32, 8 for wasm64 and other 64-bit targets)
|
||||
const ptr_size: i64 = if (tc.isWasm32()) 4 else 8;
|
||||
self.comptime_constants.put("POINTER_SIZE", .{ .int_val = ptr_size }) catch {};
|
||||
}
|
||||
@@ -437,7 +437,7 @@ pub fn dropModuleConst(self: *Lowering, source: ?[]const u8, name: []const u8) v
|
||||
/// Pass 1: Scan declarations — register ASTs and extern stubs, but don't lower bodies.
|
||||
pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// Pass 0: register every numeric-literal module const (`N :: 16` and the
|
||||
// typed `N : s64 : 16`, plus float-valued `N :: 4.0` / `N : f64 : 4.0`)
|
||||
// typed `N : i64 : 16`, plus float-valued `N :: 4.0` / `N : f64 : 4.0`)
|
||||
// BEFORE any type alias is resolved below. A type alias whose dimension is
|
||||
// a named const (`Arr :: [N]T`) resolves its dimension eagerly here, on
|
||||
// the stateless registration path; that path can only read
|
||||
@@ -455,7 +455,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
const cd = decl.data.const_decl;
|
||||
switch (cd.value.data) {
|
||||
.int_literal => {
|
||||
const info = program_index_mod.ModuleConstInfo{ .value = cd.value, .ty = .s64 };
|
||||
const info = program_index_mod.ModuleConstInfo{ .value = cd.value, .ty = .i64 };
|
||||
self.putModuleConst(decl.source_file, cd.name, info);
|
||||
},
|
||||
.float_literal => {
|
||||
@@ -465,11 +465,11 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// A const whose RHS is an integer EXPRESSION over other consts
|
||||
// (`M :: 2; N :: M + 1`) is itself a usable count: register it so
|
||||
// `moduleConstInt` can fold the RHS through `evalConstIntExpr`
|
||||
//. Placeholder `.s64` type — the count consumers read
|
||||
//. Placeholder `.i64` type — the count consumers read
|
||||
// only the value; if the expression doesn't fold (references a
|
||||
// non-const), `moduleConstInt` yields null and the use diagnoses.
|
||||
.binary_op, .unary_op => {
|
||||
const info = program_index_mod.ModuleConstInfo{ .value = cd.value, .ty = .s64 };
|
||||
const info = program_index_mod.ModuleConstInfo{ .value = cd.value, .ty = .i64 };
|
||||
self.putModuleConst(decl.source_file, cd.name, info);
|
||||
},
|
||||
else => {},
|
||||
@@ -565,7 +565,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
cd.value.data == .optional_type_expr or
|
||||
cd.value.data == .function_type_expr)
|
||||
{
|
||||
// Type alias: MyFloat :: f64; Ptr :: *u8; Cb :: (s32) -> s32;
|
||||
// Type alias: MyFloat :: f64; Ptr :: *u8; Cb :: (i32) -> i32;
|
||||
const target_ty = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
// The stateless resolver yields `.unresolved` for a shape
|
||||
// it cannot build — e.g. `Arr :: [<computed>]T`, whose
|
||||
@@ -651,7 +651,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// A namespaced callee (`ns.Box(..)`) is an explicit qualified
|
||||
// reach, exempt from the bare-head visibility gate (E4).
|
||||
const head_qualified = call_data.callee.data == .field_access;
|
||||
// A qualified head `ABox :: a.Box(s64)` selects a's OWN
|
||||
// A qualified head `ABox :: a.Box(i64)` selects a's OWN
|
||||
// template via the namespace edge (mirrors the annotation
|
||||
// head site `resolveTypeCallWithBindings`), not the bare
|
||||
// last-wins `struct_template_map`.
|
||||
@@ -660,8 +660,8 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
else
|
||||
null;
|
||||
if (callee_name.len > 0) {
|
||||
// Generic-struct alias head (`ABox :: Box(s64)` /
|
||||
// `a.Box(s64)`): route layout selection through the single
|
||||
// Generic-struct alias head (`ABox :: Box(i64)` /
|
||||
// `a.Box(i64)`): route layout selection through the single
|
||||
// choke-point (CP-1); the Vector / type-fn branches stay
|
||||
// as the non-generic fall-through.
|
||||
switch (self.selectGenericStructHead(callee_name, qual_alias, head_qualified, call_data.callee.span)) {
|
||||
@@ -698,7 +698,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
const pt = &cd.value.data.parameterized_type_expr;
|
||||
const base_name = if (std.mem.lastIndexOfScalar(u8, pt.name, '.')) |dot| pt.name[dot + 1 ..] else pt.name;
|
||||
const pt_qualified = std.mem.indexOfScalar(u8, pt.name, '.') != null;
|
||||
// A qualified base `ABox :: a.Box(s64)` selects a's OWN
|
||||
// A qualified base `ABox :: a.Box(i64)` selects a's OWN
|
||||
// template via the namespace edge (mirrors the annotation
|
||||
// head site `resolveParameterizedWithBindings`), not the
|
||||
// bare last-wins `struct_template_map`.
|
||||
@@ -723,7 +723,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
}
|
||||
// comptime_expr handled in Pass 2
|
||||
|
||||
// Typed value constants (`AF_INET :s32: 2`) are registered in
|
||||
// Typed value constants (`AF_INET :i32: 2`) are registered in
|
||||
// pass 2 below — after the forward-alias fixpoint — so a
|
||||
// forward identifier alias in the annotation resolves to its
|
||||
// target instead of a fabricated stub. Untyped
|
||||
@@ -733,7 +733,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// Untyped literal constants (e.g. UI_VERT_SRC :: #string GLSL...GLSL;)
|
||||
const lit_ty: ?TypeId = switch (cd.value.data) {
|
||||
.string_literal => .string,
|
||||
.int_literal => .s64,
|
||||
.int_literal => .i64,
|
||||
.float_literal => .f64,
|
||||
.bool_literal => .bool,
|
||||
// Complex constant expressions (e.g. COLOR_WHITE :: Color.{ r = 255, ... })
|
||||
@@ -825,19 +825,19 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
.own_opaque, .ambiguous, .none => false,
|
||||
};
|
||||
if (!recv_is_agg) continue;
|
||||
self.putModuleConst(decl.source_file, cd.name, .{ .value = cd.value, .ty = .s64 });
|
||||
self.putModuleConst(decl.source_file, cd.name, .{ .value = cd.value, .ty = .i64 });
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a typed module-level value constant (`AF_INET :s32: 2`). Run in
|
||||
/// Register a typed module-level value constant (`AF_INET :i32: 2`). Run in
|
||||
/// scanDecls pass 2 (after `resolveForwardIdentifierAliases`) so a forward
|
||||
/// identifier alias in the annotation (`A :: B; B :: s32; K : A : 42;`)
|
||||
/// identifier alias in the annotation (`A :: B; B :: i32; K : A : 42;`)
|
||||
/// resolves to its target rather than a fabricated empty-struct stub, which
|
||||
/// would otherwise mistype the constant.
|
||||
pub fn registerTypedModuleConst(self: *Lowering, cd: *const ast.ConstDecl) void {
|
||||
const ta = cd.type_annotation orelse return;
|
||||
// Only initializer shapes that pass 0 (binary_op / unary_op → placeholder
|
||||
// `.s64`) or the literal path register as a USABLE module const need
|
||||
// `.i64`) or the literal path register as a USABLE module const need
|
||||
// reconciling against the annotation. Every other shape (call,
|
||||
// struct/array literal, bare identifier) is never registered as a
|
||||
// foldable / emittable const, so it cannot manifest a
|
||||
@@ -860,7 +860,7 @@ pub fn registerTypedModuleConst(self: *Lowering, cd: *const ast.ConstDecl) void
|
||||
// silently-accepted const — registering it would let `emitModuleConst`
|
||||
// stamp the value with the wrong IR type (an int emitted as a `string`
|
||||
// const → a bogus pointer that segfaults at the use site) and let the
|
||||
// count path fold it (`[N]s64` → 4). Issue 0088.
|
||||
// count path fold it (`[N]i64` → 4). Issue 0088.
|
||||
if (!self.typedConstInitFits(cd.value, ty)) {
|
||||
// A non-integral compile-time float into an integer const is the
|
||||
// same implicit-narrowing failure as a typed local/field/param —
|
||||
@@ -880,7 +880,7 @@ pub fn registerTypedModuleConst(self: *Lowering, cd: *const ast.ConstDecl) void
|
||||
});
|
||||
}
|
||||
// Evict the pass-0 placeholder (`N : string : 4` and
|
||||
// `N : string : M + 2` are both pre-registered as `.s64` in scanDecls
|
||||
// `N : string : M + 2` are both pre-registered as `.i64` in scanDecls
|
||||
// pass 0); leaving it would let a count use still fold `N`.
|
||||
self.dropModuleConst(self.current_source_file, cd.name);
|
||||
return;
|
||||
@@ -904,13 +904,13 @@ pub fn registerTypedModuleConst(self: *Lowering, cd: *const ast.ConstDecl) void
|
||||
/// unsound as a compile-time literal-representability oracle here — a `null`
|
||||
/// literal's natural type is `.void`, so `classify(.void, *T)` yields `.none`
|
||||
/// and would reject the valid `P : *void : null`; `bool` is 1 bit wide, so
|
||||
/// `classify(.bool, s64)` yields `.widen` and would accept the bogus
|
||||
/// `B : s64 : true`.
|
||||
/// `classify(.bool, i64)` yields `.widen` and would accept the bogus
|
||||
/// `B : i64 : true`.
|
||||
pub fn typedConstInitFits(self: *Lowering, value: *const Node, dst_ty: TypeId) bool {
|
||||
// An INTEGER-annotated constant accepts a compile-time INTEGRAL float —
|
||||
// a literal (`K : s64 : 4.0`), an int-leaf expression (`K : s64 : M + 2.0`
|
||||
// a literal (`K : i64 : 4.0`), an int-leaf expression (`K : i64 : M + 2.0`
|
||||
// → 4), or a float-const-leaf expression whose SUM is integral
|
||||
// (`F : f64 : 2.5; K : s64 : F + 1.5` → 4). Integrality is judged on the
|
||||
// (`F : f64 : 2.5; K : i64 : F + 1.5` → 4). Integrality is judged on the
|
||||
// FLOAT fold (`evalConstFloatExpr` + `floatToIntExact`) — the SAME facility
|
||||
// the typed-local path (`foldComptimeFloatInit`) uses — not the int-only
|
||||
// folder, which folds leaf-by-leaf in `i64` and so misses an integral SUM
|
||||
@@ -969,7 +969,7 @@ pub fn constExprInitFits(self: *Lowering, init_ty: TypeId, dst_ty: TypeId) bool
|
||||
return init_ty == dst_ty;
|
||||
}
|
||||
|
||||
/// Register an array-typed `::` constant (`K : [4]s64 : .[...]`, or the
|
||||
/// Register an array-typed `::` constant (`K : [4]i64 : .[...]`, or the
|
||||
/// untyped `A :: .[1, 2, 3]`) as an IMMUTABLE module global: one storage,
|
||||
/// reads GEP it, the emitter marks it LLVMSetGlobalConstant, dead-global
|
||||
/// elimination drops it when unused. Source-aware reads come for free via
|
||||
@@ -1016,7 +1016,7 @@ pub fn registerConstArrayGlobal(self: *Lowering, cd: *const ast.ConstDecl) void
|
||||
}
|
||||
|
||||
/// Infer `[N]T` for an untyped array-literal constant. Element types unify:
|
||||
/// all ints → s64; ANY float promotes the element type to f64 (ints convert
|
||||
/// all ints → i64; ANY float promotes the element type to f64 (ints convert
|
||||
/// exactly — the int+float promotion rule for consts, element-wise); bool /
|
||||
/// string homogeneous only. A non-numeric mix or a non-inferable element
|
||||
/// shape (nested aggregate, enum literal, named const) asks for an
|
||||
@@ -1024,18 +1024,18 @@ pub fn registerConstArrayGlobal(self: *Lowering, cd: *const ast.ConstDecl) void
|
||||
pub fn inferConstArrayType(self: *Lowering, name: []const u8, elements: []const *const Node, span: ast.Span) ?TypeId {
|
||||
if (elements.len == 0) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, span, "constant '{s}' is an empty array literal — annotate the type (e.g. `{s} : [0]s64 : .[]`)", .{ name, name });
|
||||
d.addFmt(.err, span, "constant '{s}' is an empty array literal — annotate the type (e.g. `{s} : [0]i64 : .[]`)", .{ name, name });
|
||||
return null;
|
||||
}
|
||||
var elem_ty: ?TypeId = null;
|
||||
for (elements) |e| {
|
||||
const leaf: ?TypeId = switch (e.data) {
|
||||
.int_literal => .s64,
|
||||
.int_literal => .i64,
|
||||
.float_literal => .f64,
|
||||
.bool_literal => .bool,
|
||||
.string_literal => .string,
|
||||
.unary_op => |uo| if (uo.op == .negate) switch (uo.operand.data) {
|
||||
.int_literal => .s64,
|
||||
.int_literal => .i64,
|
||||
.float_literal => .f64,
|
||||
else => null,
|
||||
} else null,
|
||||
@@ -1049,7 +1049,7 @@ pub fn inferConstArrayType(self: *Lowering, name: []const u8, elements: []const
|
||||
if (elem_ty) |prev| {
|
||||
if (prev == lt) continue;
|
||||
// Numeric mix promotes to the float element type.
|
||||
const numeric_pair = (prev == .s64 and lt == .f64) or (prev == .f64 and lt == .s64);
|
||||
const numeric_pair = (prev == .i64 and lt == .f64) or (prev == .f64 and lt == .i64);
|
||||
if (numeric_pair) {
|
||||
elem_ty = .f64;
|
||||
continue;
|
||||
@@ -1089,7 +1089,7 @@ pub fn maybeRegisterConstStructGlobal(self: *Lowering, cd: *const ast.ConstDecl)
|
||||
|
||||
/// Register a top-level mutable global (e.g., `context : Context = ---;`).
|
||||
/// Run AFTER `resolveForwardIdentifierAliases` so a forward identifier alias
|
||||
/// in the type annotation (`A :: B; B :: s32; g : A = 7;`) resolves to its
|
||||
/// in the type annotation (`A :: B; B :: i32; g : A = 7;`) resolves to its
|
||||
/// target instead of a fabricated empty-struct stub, which would otherwise
|
||||
/// give the global a type that mismatches its initializer at LLVM
|
||||
/// verification. Globals can't be named in a type position, so
|
||||
@@ -1143,7 +1143,7 @@ pub fn globalInitValue(self: *Lowering, vd: *const ast.VarDecl, var_ty: TypeId)
|
||||
self.checkIntLiteralFits(il.value, var_ty, v.span);
|
||||
break :blk .{ .int = il.value };
|
||||
},
|
||||
// A negated literal (`g : s64 = -1;`) folds through the shared
|
||||
// A negated literal (`g : i64 = -1;`) folds through the shared
|
||||
// const-expr serializer. The folded value follows the same rules as
|
||||
// the direct literal arms: int fits-check; a float at an integer
|
||||
// global narrows only when integral.
|
||||
@@ -1239,8 +1239,8 @@ pub fn diagnoseNonConstGlobal(self: *Lowering, vd: *const ast.VarDecl, v: *const
|
||||
/// is already resolved as a type author; a forward target isn't yet present,
|
||||
/// so `A` is left unregistered and its uses get falsely flagged as an unknown
|
||||
/// type. Re-resolve to a fixpoint now that every top-level name
|
||||
/// has been seen, so `A :: B; B :: s32;` converges the same as the ordered
|
||||
/// `B :: s32; A :: B;`. A value const is never an `.identifier` node
|
||||
/// has been seen, so `A :: B; B :: i32;` converges the same as the ordered
|
||||
/// `B :: i32; A :: B;`. A value const is never an `.identifier` node
|
||||
/// (`NotAType :: 123` is an int literal), and an alias whose target is a value
|
||||
/// const stays unresolved, so neither this pass nor the unknown-type suppression can register a
|
||||
/// non-type name.
|
||||
@@ -1274,8 +1274,8 @@ pub fn resolveForwardIdentifierAliases(self: *Lowering, decls: []const *const No
|
||||
if (self.aliasResolvedInSource(src, cd.name)) continue;
|
||||
const rhs = cd.value.data.identifier;
|
||||
// Pass the backtick raw flag so a forward alias whose RHS is a raw
|
||||
// identifier (`` RawAlias :: `s2 ``, target declared later) resolves
|
||||
// to the nominal `` `s2 `` author, not the builtin `s2` spelling.
|
||||
// identifier (`` RawAlias :: `i2 ``, target declared later) resolves
|
||||
// to the nominal `` `i2 `` author, not the builtin `i2` spelling.
|
||||
switch (self.selectNominalLeaf(rhs.name, src, rhs.is_raw)) {
|
||||
.resolved => |tid| {
|
||||
self.putTypeAlias(decl.source_file, cd.name, tid);
|
||||
@@ -1934,9 +1934,9 @@ pub fn structMethodFn(sd: *const ast.StructDecl, method: []const u8) ?*const ast
|
||||
|
||||
/// TRUE iff `ref` is a TYPE-FUNCTION head author — a `fn_decl` (or const-
|
||||
/// wrapped fn) declaring at least one `$`-parameter, i.e. instantiable as a
|
||||
/// bare type head (`Make(s64)` where `Make :: ($T) -> Type`). Mirrors the
|
||||
/// bare type head (`Make(i64)` where `Make :: ($T) -> Type`). Mirrors the
|
||||
/// `fd.type_params.len > 0` gate every instantiation site uses to recognize a
|
||||
/// type-fn head, so an ORDINARY same-name function (`Make :: () -> s32`, zero
|
||||
/// type-fn head, so an ORDINARY same-name function (`Make :: () -> i32`, zero
|
||||
/// type params) is NOT a type-fn author and does NOT vouch for a hidden 2-flat-
|
||||
/// hop type-fn head (E4 attempt-8: a `fn_decl != null` author view let any
|
||||
/// visible function — type-fn or not — authorize a type head).
|
||||
@@ -2262,7 +2262,7 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
// Defer functions with type-category matches until all types are registered.
|
||||
// any_to_string uses `if type == { case slice: ... }` which compiles a switch
|
||||
// with type tags from resolveTypeCategoryTags. This must happen AFTER main is
|
||||
// fully lowered so all types ([]s32, List__s32, etc.) are in the TypeTable.
|
||||
// fully lowered so all types ([]i32, List__i32, etc.) are in the TypeTable.
|
||||
if (!self.processing_deferred and std.mem.eql(u8, name, "any_to_string")) {
|
||||
self.deferred_type_fns.append(self.alloc, name) catch {};
|
||||
return;
|
||||
@@ -2557,8 +2557,8 @@ pub fn emitModuleConst(self: *Lowering, ci: ModuleConstInfo, author_source: ?[]c
|
||||
// accepted under the unified narrowing rule — materializes as its folded
|
||||
// int through the SAME `program_index.foldCountI64` the count / array-dim
|
||||
// path uses, so the const's emitted VALUE and its use as a COUNT come from
|
||||
// one fold (`K : s64 : 4.0` → 4; `K : s64 : M + 2.0` → 4; and a float-const-
|
||||
// leaf `KF : s64 : F + 1.5` → 4, which the int-only folder could not reach).
|
||||
// one fold (`K : i64 : 4.0` → 4; `K : i64 : M + 2.0` → 4; and a float-const-
|
||||
// leaf `KF : i64 : F + 1.5` → 4, which the int-only folder could not reach).
|
||||
// A non-integral float never arrives (it was rejected at registration); any
|
||||
// other non-foldable shape falls through to the per-kind emitters below.
|
||||
if (self.isIntEx(ci.ty)) {
|
||||
@@ -2597,5 +2597,5 @@ pub fn emitModuleConst(self: *Lowering, ci: ModuleConstInfo, author_source: ?[]c
|
||||
|
||||
pub fn emitPlaceholder(self: *Lowering, name: []const u8) Ref {
|
||||
const sid = self.module.types.internString(name);
|
||||
return self.builder.emit(.{ .placeholder = sid }, .s64);
|
||||
return self.builder.emit(.{ .placeholder = sid }, .i64);
|
||||
}
|
||||
|
||||
@@ -428,8 +428,8 @@ pub fn lowerCallerLocation(self: *Lowering, node: *const Node) Ref {
|
||||
const func_name = self.currentFunctionName();
|
||||
var fields = [_]Ref{
|
||||
self.builder.constString(self.module.types.internString(file)),
|
||||
self.builder.constInt(@intCast(loc.line), .s32),
|
||||
self.builder.constInt(@intCast(loc.col), .s32),
|
||||
self.builder.constInt(@intCast(loc.line), .i32),
|
||||
self.builder.constInt(@intCast(loc.col), .i32),
|
||||
self.builder.constString(self.module.types.internString(func_name)),
|
||||
};
|
||||
return self.builder.emit(.{ .struct_init = .{ .fields = self.alloc.dupe(Ref, &fields) catch unreachable } }, sl_tid);
|
||||
|
||||
@@ -82,7 +82,7 @@ pub fn lowerStructLiteral(self: *Lowering, sl: *const ast.StructLiteral, span: a
|
||||
// `.ambiguous`/`.not_visible` surface their loud diagnostic + poison.
|
||||
self.resolveNominalLeaf(name, false, span)
|
||||
else if (sl.type_expr) |te|
|
||||
// Generic struct literal: Pair(s32).{ ... } — resolve type from type_expr
|
||||
// Generic struct literal: Pair(i32).{ ... } — resolve type from type_expr
|
||||
self.resolveTypeWithBindings(te)
|
||||
else self.target_type orelse .unresolved;
|
||||
|
||||
@@ -315,7 +315,7 @@ pub fn fixupMethodReceiver(self: *Lowering, method_args: *std.ArrayList(Ref), fu
|
||||
/// Get the name of a struct type (dereferencing pointers). Returns null for non-struct types.
|
||||
pub fn getStructTypeName(self: *Lowering, ty: TypeId) ?[]const u8 {
|
||||
if (ty.isBuiltin()) {
|
||||
// Map builtin types to their names for method resolution (e.g., s64.eq)
|
||||
// Map builtin types to their names for method resolution (e.g., i64.eq)
|
||||
return builtinTypeName(ty);
|
||||
}
|
||||
var resolved = ty;
|
||||
@@ -333,10 +333,10 @@ pub fn getStructTypeName(self: *Lowering, ty: TypeId) ?[]const u8 {
|
||||
|
||||
pub fn builtinTypeName(ty: TypeId) ?[]const u8 {
|
||||
return switch (ty) {
|
||||
.s8 => "s8",
|
||||
.s16 => "s16",
|
||||
.s32 => "s32",
|
||||
.s64 => "s64",
|
||||
.i8 => "i8",
|
||||
.i16 => "i16",
|
||||
.i32 => "i32",
|
||||
.i64 => "i64",
|
||||
.u8 => "u8",
|
||||
.u16 => "u16",
|
||||
.u32 => "u32",
|
||||
@@ -351,7 +351,7 @@ pub fn builtinTypeName(ty: TypeId) ?[]const u8 {
|
||||
|
||||
/// Resolve the type of a named field on a given type.
|
||||
pub fn resolveFieldType(self: *Lowering, ty: TypeId, field: []const u8) TypeId {
|
||||
if (std.mem.eql(u8, field, "len")) return .s64;
|
||||
if (std.mem.eql(u8, field, "len")) return .i64;
|
||||
if (std.mem.eql(u8, field, "ptr")) {
|
||||
const elem_ty = self.getElementType(ty);
|
||||
return self.module.types.manyPtrTo(elem_ty);
|
||||
@@ -497,7 +497,7 @@ pub fn lowerFieldAccess(self: *Lowering, fa: *const ast.FieldAccess, span: ast.S
|
||||
if (self.pack_param_count) |ppc| {
|
||||
if (fa.object.data == .identifier and std.mem.eql(u8, fa.field, "len")) {
|
||||
if (ppc.get(fa.object.data.identifier.name)) |n| {
|
||||
return self.builder.constInt(@as(i64, @intCast(n)), .s64);
|
||||
return self.builder.constInt(@as(i64, @intCast(n)), .i64);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -606,7 +606,7 @@ pub fn lowerFieldAccess(self: *Lowering, fa: *const ast.FieldAccess, span: ast.S
|
||||
|
||||
if (is_special) {
|
||||
if (std.mem.eql(u8, fa.field, "len")) {
|
||||
return self.builder.emit(.{ .length = .{ .operand = obj } }, .s64);
|
||||
return self.builder.emit(.{ .length = .{ .operand = obj } }, .i64);
|
||||
}
|
||||
{
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
@@ -658,7 +658,7 @@ pub fn identifierBindsValue(self: *Lowering, name: []const u8) bool {
|
||||
/// a builtin type (a user struct → ordinary field lowering reports
|
||||
/// field-not-found). Two clean diagnostics (then a placeholder, so lowering
|
||||
/// finishes and `hasErrors()` aborts the build):
|
||||
/// - a FLOAT-only accessor on an integer type (`s32.epsilon`, `u8.inf`);
|
||||
/// - a FLOAT-only accessor on an integer type (`i32.epsilon`, `u8.inf`);
|
||||
/// - any accessor on a builtin NON-numeric receiver
|
||||
/// (`bool`/`string`/`void`/`Any`/`noreturn`).
|
||||
pub fn lowerNumericLimit(self: *Lowering, fa: *const ast.FieldAccess, span: ast.Span) ?Ref {
|
||||
@@ -704,7 +704,7 @@ pub fn lowerStructConstant(self: *Lowering, info: StructConstInfo) Ref {
|
||||
return switch (val_node.data) {
|
||||
.int_literal => |lit| blk: {
|
||||
if (info.ty) |t| self.checkIntLiteralFits(lit.value, t, val_node.span);
|
||||
break :blk self.builder.constInt(lit.value, info.ty orelse .s64);
|
||||
break :blk self.builder.constInt(lit.value, info.ty orelse .i64);
|
||||
},
|
||||
.float_literal => |lit| self.builder.constFloat(lit.value, info.ty orelse .f64),
|
||||
.bool_literal => |lit| self.builder.constBool(lit.value),
|
||||
@@ -1199,9 +1199,9 @@ pub fn resolveArrayLiteralType(self: *Lowering, te: *const Node) TypeId {
|
||||
return self.module.types.vectorOf(elem, length);
|
||||
}
|
||||
}
|
||||
// Generic-struct typed-literal head (`Box(s64).[...]`): route
|
||||
// Generic-struct typed-literal head (`Box(i64).[...]`): route
|
||||
// through the single layout choke-point (CP-1). A qualified head
|
||||
// `a.Box(s64).[...]` selects a's OWN template via the namespace edge
|
||||
// `a.Box(i64).[...]` selects a's OWN template via the namespace edge
|
||||
// (Counter-1: was the global last-wins map); a bare head selects the
|
||||
// single bare-VISIBLE author.
|
||||
if (headNameOfCallee(cl.callee)) |hn| {
|
||||
@@ -1258,7 +1258,7 @@ pub fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
// bounds" instead of the generic "unresolved 'args'" that the
|
||||
// fall-through scope-lookup would produce.
|
||||
if (self.diagPackIndexOOB(ie)) {
|
||||
return self.builder.constInt(0, .s64);
|
||||
return self.builder.constInt(0, .i64);
|
||||
}
|
||||
// Runtime index into a comptime-only pack (Decision 1): a pack has no
|
||||
// runtime representation, so the index must be a compile-time constant.
|
||||
@@ -1271,7 +1271,7 @@ pub fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
if (self.diagnostics) |diags| {
|
||||
diags.addFmt(.err, ie.index.span, "pack '{s}' must be indexed by a compile-time constant — a pack is comptime-only and has no runtime value", .{pname});
|
||||
}
|
||||
return self.builder.constInt(0, .s64);
|
||||
return self.builder.constInt(0, .i64);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1306,10 +1306,10 @@ pub fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
|
||||
pub fn lowerSliceExpr(self: *Lowering, se: *const ast.SliceExpr) Ref {
|
||||
const obj = self.lowerExpr(se.object);
|
||||
var lo = if (se.start) |s| self.lowerExpr(s) else self.builder.constInt(0, .s64);
|
||||
if (se.start_exclusive) lo = self.builder.add(lo, self.builder.constInt(1, .s64), .s64);
|
||||
var hi = if (se.end) |e| self.lowerExpr(e) else self.builder.emit(.{ .length = .{ .operand = obj } }, .s64);
|
||||
if (se.end_inclusive) hi = self.builder.add(hi, self.builder.constInt(1, .s64), .s64);
|
||||
var lo = if (se.start) |s| self.lowerExpr(s) else self.builder.constInt(0, .i64);
|
||||
if (se.start_exclusive) lo = self.builder.add(lo, self.builder.constInt(1, .i64), .i64);
|
||||
var hi = if (se.end) |e| self.lowerExpr(e) else self.builder.emit(.{ .length = .{ .operand = obj } }, .i64);
|
||||
if (se.end_inclusive) hi = self.builder.add(hi, self.builder.constInt(1, .i64), .i64);
|
||||
// Infer result slice type from the object
|
||||
const obj_ty = self.inferExprType(se.object);
|
||||
// Subslice of string stays string (same {ptr, i64} layout, correct type category)
|
||||
@@ -1598,8 +1598,8 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
}
|
||||
}
|
||||
const ty = if (self.target_type) |tt| blk: {
|
||||
break :blk if (self.isIntEx(tt)) tt else .s64;
|
||||
} else .s64;
|
||||
break :blk if (self.isIntEx(tt)) tt else .i64;
|
||||
} else .i64;
|
||||
self.checkIntLiteralFits(lit.value, ty, node.span);
|
||||
return self.builder.constInt(lit.value, ty);
|
||||
},
|
||||
@@ -1645,7 +1645,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
// Check compile-time constants (OS, ARCH, POINTER_SIZE) before globals
|
||||
if (self.comptime_constants.get(id.name)) |cv| {
|
||||
switch (cv) {
|
||||
.int_val => |iv| break :blk self.builder.constInt(iv, .s64),
|
||||
.int_val => |iv| break :blk self.builder.constInt(iv, .i64),
|
||||
.enum_tag => |et| break :blk self.builder.constInt(@intCast(et.tag), et.ty),
|
||||
}
|
||||
}
|
||||
@@ -1683,7 +1683,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
.untracked => break :blk self.builder.emit(.{ .global_get = gi.id }, gi.ty),
|
||||
}
|
||||
}
|
||||
// Check module-level value constants (e.g. AF_INET :s32: 2)
|
||||
// Check module-level value constants (e.g. AF_INET :i32: 2)
|
||||
if (self.program_index.module_const_map.get(id.name)) |ci_global| {
|
||||
if (!self.isNameVisible(id.name)) {
|
||||
if (self.diagnostics) |d|
|
||||
@@ -1820,7 +1820,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
// examples/50-smoke.sx has both shapes.
|
||||
}
|
||||
}
|
||||
break :blk self.builder.emit(.{ .func_ref = fid }, .s64);
|
||||
break :blk self.builder.emit(.{ .func_ref = fid }, .i64);
|
||||
}
|
||||
}
|
||||
// Type-as-value: a name that resolves to a TypeId
|
||||
@@ -1927,7 +1927,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
break :blk self.builder.constFloat(@floatFromInt(v), tt);
|
||||
}
|
||||
}
|
||||
const nty = if (self.target_type) |tt| (if (self.isIntEx(tt)) tt else TypeId.s64) else TypeId.s64;
|
||||
const nty = if (self.target_type) |tt| (if (self.isIntEx(tt)) tt else TypeId.i64) else TypeId.i64;
|
||||
self.checkIntLiteralFits(v, nty, node.span);
|
||||
break :blk self.builder.constInt(v, nty);
|
||||
}
|
||||
@@ -2031,7 +2031,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
},
|
||||
|
||||
// type_expr can appear as a variable reference when the name collides
|
||||
// with a builtin type name (e.g. s2, u8). Check scope first.
|
||||
// with a builtin type name (e.g. i2, u8). Check scope first.
|
||||
.type_expr => |te| blk: {
|
||||
if (self.scope) |scope| {
|
||||
if (scope.lookup(te.name)) |binding| {
|
||||
@@ -2057,8 +2057,8 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
|
||||
// Compound type literals (`*T`, `[]T`, `[*]T`, `?T`, `[N]T`, fn types)
|
||||
// in expression position are first-class `Type` values, exactly like
|
||||
// the named form above (`t : Type = *s64;` ↔ `t : Type = f64;`). Also
|
||||
// the path a static `cast(*s64) v` type argument takes — call args are
|
||||
// the named form above (`t : Type = *i64;` ↔ `t : Type = f64;`). Also
|
||||
// the path a static `cast(*i64) v` type argument takes — call args are
|
||||
// lowered before the cast handler inspects the AST (issue 0118).
|
||||
.pointer_type_expr,
|
||||
.many_pointer_type_expr,
|
||||
@@ -2128,7 +2128,7 @@ pub fn lowerBinaryOp(self: *Lowering, bop: *const ast.BinaryOp) Ref {
|
||||
}
|
||||
|
||||
// Type-literal comparison fold: when both sides are type-shaped
|
||||
// AST nodes (`s64`, `*u8`, `?T`, `[3]f64`, etc.) OR resolve to
|
||||
// AST nodes (`i64`, `*u8`, `?T`, `[3]f64`, etc.) OR resolve to
|
||||
// a static TypeId at lower time (`type_of(x)` for any
|
||||
// statically-typed `x`), resolve each and emit a `const_bool`.
|
||||
// Same semantic as `type_eq(A, B)` but using the standard `==`
|
||||
@@ -2144,11 +2144,11 @@ pub fn lowerBinaryOp(self: *Lowering, bop: *const ast.BinaryOp) Ref {
|
||||
}
|
||||
}
|
||||
|
||||
// Any-shaped `==` (e.g. `t == s64` where `t: Type`): both
|
||||
// Any-shaped `==` (e.g. `t == i64` where `t: Type`): both
|
||||
// operands are 16-byte `{tag, value}` aggregates. LLVM
|
||||
// doesn't accept `icmp` on aggregates directly. Decompose
|
||||
// via `unbox_any` (which extracts the value field at
|
||||
// `.s64`) and compare the i64s. Tag fields are stable
|
||||
// `.i64`) and compare the i64s. Tag fields are stable
|
||||
// across compilations of the same source so value-only
|
||||
// identity is enough.
|
||||
if (bop.op == .eq or bop.op == .neq) {
|
||||
@@ -2157,8 +2157,8 @@ pub fn lowerBinaryOp(self: *Lowering, bop: *const ast.BinaryOp) Ref {
|
||||
if (lhs_ty == .any and rhs_ty == .any) {
|
||||
const lhs = self.lowerExpr(bop.lhs);
|
||||
const rhs = self.lowerExpr(bop.rhs);
|
||||
const lhs_val = self.builder.emit(.{ .unbox_any = .{ .operand = lhs } }, .s64);
|
||||
const rhs_val = self.builder.emit(.{ .unbox_any = .{ .operand = rhs } }, .s64);
|
||||
const lhs_val = self.builder.emit(.{ .unbox_any = .{ .operand = lhs } }, .i64);
|
||||
const rhs_val = self.builder.emit(.{ .unbox_any = .{ .operand = rhs } }, .i64);
|
||||
if (bop.op == .eq) {
|
||||
return self.builder.emit(.{ .cmp_eq = .{ .lhs = lhs_val, .rhs = rhs_val } }, .bool);
|
||||
} else {
|
||||
@@ -2249,7 +2249,7 @@ pub fn lowerBinaryOp(self: *Lowering, bop: *const ast.BinaryOp) Ref {
|
||||
if (rhs_ref_pointee) |p| rhs = self.builder.load(rhs, p);
|
||||
self.target_type = saved_tt;
|
||||
// Result type follows the shared promotion rule: an int LHS with a
|
||||
// float RHS promotes to the float (`s64 * f32` → `f32`); vectors /
|
||||
// float RHS promotes to the float (`i64 * f32` → `f32`); vectors /
|
||||
// structs keep the LHS type. `inferExprType` reuses the same helper
|
||||
// so static typing agrees with the value produced here.
|
||||
const rhs_inferred = rhs_ref_pointee orelse self.inferExprType(bop.rhs);
|
||||
@@ -2298,7 +2298,7 @@ pub fn lowerBinaryOp(self: *Lowering, bop: *const ast.BinaryOp) Ref {
|
||||
}
|
||||
|
||||
// Reject scalar ops on incompatible operand types (e.g.
|
||||
// `s64 + string`, `s64 < string`, `s64 & string`). The result type
|
||||
// `i64 + string`, `i64 < string`, `i64 & string`). The result type
|
||||
// `ty` is derived from the LHS, so without this the op lowers as
|
||||
// `<op> : <lhs>` and either reinterprets the RHS bytes (arithmetic
|
||||
// / bitwise → garbage) or feeds mismatched LLVM types to `icmp`
|
||||
@@ -2448,7 +2448,7 @@ pub fn lowerTupleOp(self: *Lowering, bop: *const ast.BinaryOp, lhs: Ref, rhs: Re
|
||||
// Lexicographic comparison
|
||||
return self.lowerTupleLexCompare(bop.op, lhs, rhs, lhs_fields);
|
||||
},
|
||||
else => return self.builder.constInt(0, .s64),
|
||||
else => return self.builder.constInt(0, .i64),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -318,14 +318,14 @@ pub fn lowerForeignMethodCall(
|
||||
const ret_ty = if (method.return_type) |rt| self.resolveType(rt) else .void;
|
||||
|
||||
// Reject return types the JNI emit path can't dispatch — emit_llvm's
|
||||
// Call<T>Method switch only covers void / bool / s32 / s64 / f32 / f64
|
||||
// / pointer-returning. Anything else (s8 / s16 / u8 / u16 / aggregates)
|
||||
// Call<T>Method switch only covers void / bool / i32 / i64 / f32 / f64
|
||||
// / pointer-returning. Anything else (i8 / i16 / u8 / u16 / aggregates)
|
||||
// would silently lower to LLVMGetUndef and produce wrong arguments at
|
||||
// the call site (chess Android touch shipped broken because s32→s32+
|
||||
// the call site (chess Android touch shipped broken because i32→i32+
|
||||
// f32 returns hit the undef path before .f32 was wired up).
|
||||
if (!jni_descriptor.isJniReturnTypeSupported(&self.module.types, ret_ty)) {
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, span, "JNI method '{s}.{s}' returns '{s}', which isn't supported by the JNI call-method lowering yet — only void/bool/s32/s64/f32/f64 and pointers are wired up", .{ fcd.name, method.name, self.module.types.typeName(ret_ty) });
|
||||
d.addFmt(.err, span, "JNI method '{s}.{s}' returns '{s}', which isn't supported by the JNI call-method lowering yet — only void/bool/i32/i64/f32/f64 and pointers are wired up", .{ fcd.name, method.name, self.module.types.typeName(ret_ty) });
|
||||
}
|
||||
return Ref.none;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ pub fn monomorphizeFunction(self: *Lowering, fd: *const ast.FnDecl, mangled_name
|
||||
/// - type_expr AST nodes
|
||||
/// True iff `node` matches an AST shape that `resolveTypeArg`
|
||||
/// can resolve to a concrete TypeId without falling through to
|
||||
/// the silent `.s64` default. Used by `tryLowerReflectionCall`
|
||||
/// the silent `.i64` default. Used by `tryLowerReflectionCall`
|
||||
/// to split static-fold from dynamic-builtin-call paths.
|
||||
///
|
||||
/// Static-arg shapes mirror the explicit arms of `resolveTypeArg`:
|
||||
@@ -206,9 +206,9 @@ pub fn monomorphizeFunction(self: *Lowering, fd: *const ast.FnDecl, mangled_name
|
||||
pub fn isStaticTypeArg(self: *Lowering, node: *const Node) bool {
|
||||
switch (node.data) {
|
||||
.type_expr => |te| {
|
||||
// A type-keyword name (e.g. `s64`) is always static.
|
||||
// A type-keyword name (e.g. `i64`) is always static.
|
||||
// A user-defined name that happens to be in scope as
|
||||
// a runtime variable (`x: Type = s64; type_name(x)`)
|
||||
// a runtime variable (`x: Type = i64; type_name(x)`)
|
||||
// is NOT static — route through the dynamic builtin
|
||||
// call so the runtime lookup table fires.
|
||||
if (self.scope) |scope| {
|
||||
@@ -245,7 +245,7 @@ pub fn isStaticTypeArg(self: *Lowering, node: *const Node) bool {
|
||||
pub fn isStaticTypeRef(self: *Lowering, node: *const Node) bool {
|
||||
switch (node.data) {
|
||||
.type_expr => |te| {
|
||||
// Compound type names (`s64`, `Point`, `Vec4`) resolve
|
||||
// Compound type names (`i64`, `Point`, `Vec4`) resolve
|
||||
// statically. If the name is also a runtime var in
|
||||
// scope, it's a value reference, not a type ref.
|
||||
if (self.scope) |scope| {
|
||||
@@ -286,10 +286,10 @@ pub fn isStaticTypeRef(self: *Lowering, node: *const Node) bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a tuple LITERAL used in a type position (`(s32, s32)` reinterpreted
|
||||
/// Resolve a tuple LITERAL used in a type position (`(i32, i32)` reinterpreted
|
||||
/// as a tuple type at a type-demanding site such as `size_of`). Every element
|
||||
/// must itself denote a type; a non-type element — e.g. the `1` in
|
||||
/// `(s32, 1)` — is a user error. Emit a diagnostic pointing at the offending
|
||||
/// `(i32, 1)` — is a user error. Emit a diagnostic pointing at the offending
|
||||
/// element and return `.unresolved`; never fabricate a tuple with a bogus
|
||||
/// field. type_bridge.resolveAstType builds the tuple only after
|
||||
/// this validation passes.
|
||||
@@ -297,12 +297,12 @@ pub fn resolveTupleLiteralTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
for (node.data.tuple_literal.elements) |el| {
|
||||
if (!type_bridge.isTypeShapedAstNode(el.value, &self.module.types)) {
|
||||
if (self.diagnostics) |diags| {
|
||||
diags.addFmt(.err, el.value.span, "tuple type element is not a type (found `{s}`); a tuple used as a type must list only types, e.g. `(s32, s32)`", .{@tagName(el.value.data)});
|
||||
diags.addFmt(.err, el.value.span, "tuple type element is not a type (found `{s}`); a tuple used as a type must list only types, e.g. `(i32, i32)`", .{@tagName(el.value.data)});
|
||||
}
|
||||
return .unresolved;
|
||||
}
|
||||
// E4 single-hop visibility gate: each element leaf is resolved through
|
||||
// the source-aware resolver, so a 2-flat-hop inner leaf (`(COnly, s64)`)
|
||||
// the source-aware resolver, so a 2-flat-hop inner leaf (`(COnly, i64)`)
|
||||
// emits "not visible" + poisons rather than leaking through
|
||||
// `type_bridge`'s ungated global lookup. A valid element resolves to the
|
||||
// same TypeId the delegated build produces below (no diagnostic, no
|
||||
@@ -314,10 +314,10 @@ pub fn resolveTupleLiteralTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
|
||||
pub fn resolveTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
// Pack-index access in a type-arg slot (e.g. `type_name($args[0])`
|
||||
// or `type_eq($args[i], s64)`). Same shape as the
|
||||
// or `type_eq($args[i], i64)`). Same shape as the
|
||||
// `resolveTypeWithBindings` arm — looks up the bound pack types
|
||||
// and returns the i-th. OOB and no-active-binding emit focused
|
||||
// diagnostics rather than silently defaulting to .s64 (the
|
||||
// diagnostics rather than silently defaulting to .i64 (the
|
||||
// catch-all `else` below) — that fall-through is exactly the
|
||||
// "silent unimplemented arm" the project's REJECTED PATTERNS
|
||||
// forbid.
|
||||
@@ -392,8 +392,8 @@ pub fn resolveTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
// time when `x`'s type is statically known (which it
|
||||
// is for any expression — type inference always
|
||||
// produces a concrete TypeId). Lets
|
||||
// `type_of(a) == s64` fold the same as
|
||||
// `inferExprType(a) == s64`.
|
||||
// `type_of(a) == i64` fold the same as
|
||||
// `inferExprType(a) == i64`.
|
||||
if (cl.callee.data == .identifier and
|
||||
std.mem.eql(u8, cl.callee.data.identifier.name, "type_of") and
|
||||
cl.args.len == 1)
|
||||
@@ -407,7 +407,7 @@ pub fn resolveTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
// route through the gated `resolveTypeWithBindings`, whose
|
||||
// `resolveCompound` recurses each element through the source-aware leaf
|
||||
// (`resolveNominalLeaf`) — so a 2-hop inner leaf (`*COnly`, `[2]COnly`,
|
||||
// `(COnly, s64)`) is rejected exactly as in a normal annotation, instead
|
||||
// `(COnly, i64)`) is rejected exactly as in a normal annotation, instead
|
||||
// of `type_bridge.resolveAstType`'s ungated global lookup (E4).
|
||||
.tuple_literal,
|
||||
.pointer_type_expr,
|
||||
@@ -421,13 +421,13 @@ pub fn resolveTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a type name for display (e.g. "*Point", "[]s32", "[3]f64").
|
||||
/// Format a type name for display (e.g. "*Point", "[]i32", "[3]f64").
|
||||
pub fn formatTypeName(self: *Lowering, ty: TypeId) []const u8 {
|
||||
// Builtin types: use their canonical name
|
||||
if (ty == .s8) return "s8";
|
||||
if (ty == .s16) return "s16";
|
||||
if (ty == .s32) return "s32";
|
||||
if (ty == .s64) return "s64";
|
||||
if (ty == .i8) return "i8";
|
||||
if (ty == .i16) return "i16";
|
||||
if (ty == .i32) return "i32";
|
||||
if (ty == .i64) return "i64";
|
||||
if (ty == .u8) return "u8";
|
||||
if (ty == .u16) return "u16";
|
||||
if (ty == .u32) return "u32";
|
||||
@@ -463,7 +463,7 @@ pub fn formatTypeName(self: *Lowering, ty: TypeId) []const u8 {
|
||||
const inner = self.formatTypeName(a.element);
|
||||
break :blk std.fmt.allocPrint(self.alloc, "[{d}]{s}", .{ a.length, inner }) catch "array";
|
||||
},
|
||||
.signed => |w| std.fmt.allocPrint(self.alloc, "s{d}", .{w}) catch "signed",
|
||||
.signed => |w| std.fmt.allocPrint(self.alloc, "i{d}", .{w}) catch "signed",
|
||||
.unsigned => |w| std.fmt.allocPrint(self.alloc, "u{d}", .{w}) catch "unsigned",
|
||||
.optional => |o| blk: {
|
||||
const inner = self.formatTypeName(o.child);
|
||||
@@ -477,7 +477,7 @@ pub fn formatTypeName(self: *Lowering, ty: TypeId) []const u8 {
|
||||
};
|
||||
}
|
||||
|
||||
/// Format a function type string like "() -> s32" or "(s32, s32) -> s32".
|
||||
/// Format a function type string like "() -> i32" or "(i32, i32) -> i32".
|
||||
pub fn formatFnTypeString(self: *Lowering, fd: *const ast.FnDecl) []const u8 {
|
||||
var buf: [512]u8 = undefined;
|
||||
var pos: usize = 0;
|
||||
@@ -509,7 +509,7 @@ pub fn formatFnTypeString(self: *Lowering, fd: *const ast.FnDecl) []const u8 {
|
||||
}
|
||||
|
||||
/// Format a type name for function name mangling (identifier-safe).
|
||||
/// E.g. *Point → "ptr_Point", []s32 → "slice_s32", [3]f64 → "array_3_f64".
|
||||
/// E.g. *Point → "ptr_Point", []i32 → "slice_i32", [3]f64 → "array_3_f64".
|
||||
/// Check if a param type expression references a type param name (possibly nested).
|
||||
pub fn matchTypeParam(_: *Lowering, type_node: *const Node, tp_name: []const u8) bool {
|
||||
return switch (type_node.data) {
|
||||
@@ -548,7 +548,7 @@ pub fn matchTypeParamStatic(type_node: *const Node, tp_name: []const u8) bool {
|
||||
}
|
||||
|
||||
/// Extract the concrete type that corresponds to a type param from an arg type.
|
||||
/// E.g., param type []$T with arg type []s64 → T = s64.
|
||||
/// E.g., param type []$T with arg type []i64 → T = i64.
|
||||
pub fn extractTypeParam(self: *Lowering, type_node: *const Node, arg_ty: TypeId, tp_name: []const u8) ?TypeId {
|
||||
return switch (type_node.data) {
|
||||
.type_expr => |te| if (std.mem.eql(u8, te.name, tp_name)) arg_ty else null,
|
||||
@@ -635,10 +635,10 @@ pub fn resolveTypeCategoryTags(self: *Lowering, name: []const u8) []const u64 {
|
||||
|
||||
// Fixed builtin categories
|
||||
if (std.mem.eql(u8, name, "int")) {
|
||||
tags.append(self.alloc, TypeId.s8.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.s16.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.s32.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.s64.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.i8.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.i16.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.i32.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.i64.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.u8.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.u16.index()) catch {};
|
||||
tags.append(self.alloc, TypeId.u32.index()) catch {};
|
||||
@@ -823,7 +823,7 @@ pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
|
||||
/// Resolve a generic value-param argument (`$K: u32`) to its compile-time
|
||||
/// integer AND verify it fits the param's declared integer type. The folded
|
||||
/// value is bound and mangled into the instantiation name, so a module/generic
|
||||
/// const arg (`Vec(N, f32)`), a const expression (`Make(M + 1, s64)`), an
|
||||
/// const arg (`Vec(N, f32)`), a const expression (`Make(M + 1, i64)`), an
|
||||
/// integral float (`Box(4.0)` → 4), and a literal (`Vec(3, f32)`) all bind the
|
||||
/// same value a literal would. An out-of-range arg (`Box(5_000_000_000)` for a
|
||||
/// `u32` param) or a non-const arg emits a clean diagnostic and returns null;
|
||||
@@ -838,8 +838,8 @@ pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
|
||||
/// `program_index.intTypeRange`; an unrecognised type folds without bounding.
|
||||
pub fn resolveValueParamArg(self: *Lowering, arg_node: *const Node, param_name: []const u8, type_name: ?[]const u8) ?i64 {
|
||||
// Resolve an ALIASED integer constraint (`$K: Count` where `Count :: u32`,
|
||||
// `$K: Small` where `Small :: s8`) to its underlying builtin so the range
|
||||
// gate below treats it exactly like `$K: u32` / `$K: s8` (an
|
||||
// `$K: Small` where `Small :: i8`) to its underlying builtin so the range
|
||||
// gate below treats it exactly like `$K: u32` / `$K: i8` (an
|
||||
// alias previously slipped past `intTypeRange`, so `Box(5_000_000_000)`
|
||||
// with `$K: Count` bound a truncated value). A non-integer / unrecognised
|
||||
// constraint yields null → no range bound (fold only), as before.
|
||||
@@ -887,7 +887,7 @@ pub fn resolveValueParamArg(self: *Lowering, arg_node: *const Node, param_name:
|
||||
|
||||
/// Resolve a generic value-param constraint type NAME to its canonical builtin
|
||||
/// integer type name, chasing a type alias (`Count :: u32` → "u32",
|
||||
/// `Small :: s8` → "s8") so an ALIASED integer constraint range-checks exactly
|
||||
/// `Small :: i8` → "i8") so an ALIASED integer constraint range-checks exactly
|
||||
/// like the builtin it names. Returns the name unchanged when it is already a
|
||||
/// builtin integer; null when it isn't an integer type (directly or via alias)
|
||||
/// — the caller then folds without a range bound rather than guessing. The
|
||||
@@ -914,8 +914,8 @@ pub fn diagValueParamRange(self: *Lowering, arg_node: *const Node, param_name: [
|
||||
|
||||
/// The poison-vs-proceed projection of `headTypeGate` for an UNQUALIFIED
|
||||
/// parameterized type HEAD that names a generic STRUCT, a parameterized
|
||||
/// PROTOCOL, or a type-returning function used as a head (`Box(s64)`,
|
||||
/// `VL(s64)`) — and the alias-registration / type-match sites that likewise
|
||||
/// PROTOCOL, or a type-returning function used as a head (`Box(i64)`,
|
||||
/// `VL(i64)`) — and the alias-registration / type-match sites that likewise
|
||||
/// only need "poison or proceed". Returns TRUE (the gate's loud diagnostic is
|
||||
/// already emitted) when the head is `.not_visible` (a 2-flat-hop leak) or
|
||||
/// `.ambiguous` (≥2 direct flat same-name authors — consistent with the leaf /
|
||||
@@ -1186,7 +1186,7 @@ pub fn flatFnAuthorAmbiguous(self: *Lowering, name: []const u8, from: []const u8
|
||||
/// analogue of `isNameVisible` for a type-fn head: a same-name 1-hop
|
||||
/// NON-function (a value const `Make :: 123`, a named type) does NOT vouch
|
||||
/// (attempt-7), and — crucially — neither does a same-name 1-hop ORDINARY
|
||||
/// function (`Make :: () -> s32`, zero `$`-params), which cannot be the type
|
||||
/// function (`Make :: () -> i32`, zero `$`-params), which cannot be the type
|
||||
/// head being instantiated (attempt-8). So a type-fn whose only directly-
|
||||
/// visible same-name author is a non-fn OR a non-type-fn — its real author 2
|
||||
/// flat hops away — is correctly invisible. Mirrors `flatFnAuthorAmbiguous`'s
|
||||
@@ -1290,7 +1290,7 @@ pub fn resolveParameterizedWithBindings(self: *Lowering, pt: *const ast.Paramete
|
||||
}
|
||||
}
|
||||
|
||||
// Parameterized protocol used as a value type (`VL(s64)`): materialize a
|
||||
// Parameterized protocol used as a value type (`VL(i64)`): materialize a
|
||||
// 16-byte protocol value with the type-arg bound (not a 0-field stub).
|
||||
if (self.program_index.protocol_ast_map.get(base_name)) |pd| {
|
||||
if (pd.type_params.len > 0) {
|
||||
@@ -1300,7 +1300,7 @@ pub fn resolveParameterizedWithBindings(self: *Lowering, pt: *const ast.Paramete
|
||||
}
|
||||
|
||||
// User-defined type-returning function used as a TYPE annotation
|
||||
// (`b : Make(N, s64)` where `Make :: ($K: u32, $T: Type) -> Type`). The
|
||||
// (`b : Make(N, i64)` where `Make :: ($K: u32, $T: Type) -> Type`). The
|
||||
// `.call`-node path (`resolveTypeCallWithBindings`) already routes here;
|
||||
// a `parameterized_type_expr` must too, or the function name falls through
|
||||
// to the empty-struct stub below and `b.field` / `b.len` fails.
|
||||
@@ -1409,7 +1409,7 @@ pub fn instantiateGenericStruct(self: *Lowering, tmpl: *const StructTemplate, ar
|
||||
// A qualified `ns.Box(..)` head can select a generic template whose bare
|
||||
// name also belongs to a DIFFERENT module's same-name template (the one
|
||||
// that won the last-wins `struct_template_map`). Both would mangle to
|
||||
// `Box__s64` and the second instantiation would alias the first's layout.
|
||||
// `Box__i64` and the second instantiation would alias the first's layout.
|
||||
// Tag the NON-canonical author's mangled name with its source so each
|
||||
// author's instantiation is a distinct type. The canonical (bare-map)
|
||||
// author keeps the untagged name — no churn for single-author generics.
|
||||
@@ -1548,7 +1548,7 @@ pub fn instantiateGenericStruct(self: *Lowering, tmpl: *const StructTemplate, ar
|
||||
table.updatePreservingKey(id, info);
|
||||
|
||||
// Bind the template name to this concrete instance so a method's
|
||||
// `self: *Combined` (the template name) resolves to `*Combined__s64_s64`
|
||||
// `self: *Combined` (the template name) resolves to `*Combined__i64_i64`
|
||||
// — otherwise `self.field` hits the 0-field generic stub.
|
||||
tb.put(tmpl.name, id) catch {};
|
||||
|
||||
@@ -1694,7 +1694,7 @@ pub fn instantiateTypeFunction(self: *Lowering, alias_name: []const u8, template
|
||||
// struct/union/enum — `return [K]T`, `Vector(K, T)`, `*T`, an alias, etc.
|
||||
// Resolve it with the value/type bindings active (so `[K]T` folds K to a
|
||||
// compile-time integer). The result is interned structurally, so
|
||||
// `Make(N, s64)`, `Make(3, s64)`, and `Make(M + 1, s64)` all yield the
|
||||
// `Make(N, i64)`, `Make(3, i64)`, and `Make(M + 1, i64)` all yield the
|
||||
// same TypeId. `.unresolved` means the return wasn't a type expression
|
||||
// (e.g. a value-returning function in a type position) → fall through to
|
||||
// the caller's fallback rather than fabricating a type.
|
||||
@@ -1741,7 +1741,7 @@ pub fn instantiateTypeUnion(self: *Lowering, alias_name: []const u8, mangled_nam
|
||||
const info: types.TypeInfo = .{ .tagged_union = .{
|
||||
.name = alias_name_id,
|
||||
.fields = variant_fields.items,
|
||||
.tag_type = .s64,
|
||||
.tag_type = .i64,
|
||||
} };
|
||||
const id = if (table.findByName(alias_name_id)) |existing| existing else table.intern(info);
|
||||
table.updatePreservingKey(id, info);
|
||||
@@ -1752,7 +1752,7 @@ pub fn instantiateTypeUnion(self: *Lowering, alias_name: []const u8, mangled_nam
|
||||
const mangled_info: types.TypeInfo = .{ .tagged_union = .{
|
||||
.name = mangled_name_id,
|
||||
.fields = variant_fields.items,
|
||||
.tag_type = .s64,
|
||||
.tag_type = .i64,
|
||||
} };
|
||||
const mid = if (table.findByName(mangled_name_id)) |existing| existing else table.intern(mangled_info);
|
||||
table.updatePreservingKey(mid, mangled_info);
|
||||
|
||||
@@ -102,7 +102,7 @@ pub fn reserveShadowEnumSlot(self: *Lowering, ed: *const ast.EnumDecl) void {
|
||||
const name_id = table.internString(ed.name);
|
||||
const nominal_id = self.shadowNominalId(name_id);
|
||||
const empty: types.TypeInfo = if (ed.variant_types.len > 0)
|
||||
.{ .tagged_union = .{ .name = name_id, .fields = &.{}, .tag_type = .s64 } }
|
||||
.{ .tagged_union = .{ .name = name_id, .fields = &.{}, .tag_type = .i64 } }
|
||||
else
|
||||
.{ .@"enum" = .{ .name = name_id, .variants = &.{} } };
|
||||
const reserved = table.internNominal(empty, nominal_id);
|
||||
@@ -534,7 +534,7 @@ pub fn bareVisibleStructTemplate(self: *Lowering, name: []const u8) ?StructTempl
|
||||
}
|
||||
|
||||
/// Instantiate a generic struct template and register the result under an
|
||||
/// alias name (`Vec3 :: Vec(3, f32)` / `ABox :: a.Box(s64)`). Shared by the
|
||||
/// alias name (`Vec3 :: Vec(3, f32)` / `ABox :: a.Box(i64)`). Shared by the
|
||||
/// `.call` and `.parameterized_type_expr` const-decl alias branches and the
|
||||
/// qualified-head selection that precedes the bare `struct_template_map`
|
||||
/// fallback in each.
|
||||
|
||||
@@ -906,10 +906,10 @@ pub fn emitObjcDefinedAllocAndInit(
|
||||
|
||||
// (3) memset(state, 0, STATE_SIZE) — zero everything including the
|
||||
// allocator slot; the next store re-writes the allocator slot.
|
||||
const memset_fid = self.ensureCRuntimeDecl("memset", &.{ ptr_void, .s32, .u64 }, ptr_void);
|
||||
const memset_fid = self.ensureCRuntimeDecl("memset", &.{ ptr_void, .i32, .u64 }, ptr_void);
|
||||
const memset_args = self.alloc.alloc(Ref, 3) catch return null;
|
||||
memset_args[0] = state;
|
||||
memset_args[1] = self.builder.constInt(0, .s32);
|
||||
memset_args[1] = self.builder.constInt(0, .i32);
|
||||
memset_args[2] = size_const;
|
||||
_ = self.builder.emit(.{ .call = .{ .callee = memset_fid, .args = memset_args } }, ptr_void);
|
||||
|
||||
|
||||
@@ -176,10 +176,10 @@ pub fn lowerPackToSlice(self: *Lowering, pack_name: []const u8, slice_ty: TypeId
|
||||
};
|
||||
const slice_slot = self.builder.alloca(slice_ty);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, self.module.types.ptrTo(elem_ty), slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, slice_ty);
|
||||
if (arg_nodes.len == 0) {
|
||||
self.builder.store(ptr_gep, self.builder.constNull(self.module.types.ptrTo(elem_ty)));
|
||||
self.builder.store(len_gep, self.builder.constInt(0, .s64));
|
||||
self.builder.store(len_gep, self.builder.constInt(0, .i64));
|
||||
return self.builder.load(slice_slot, slice_ty);
|
||||
}
|
||||
const array_ty = self.module.types.arrayOf(elem_ty, @intCast(arg_nodes.len));
|
||||
@@ -193,12 +193,12 @@ pub fn lowerPackToSlice(self: *Lowering, pack_name: []const u8, slice_ty: TypeId
|
||||
} else if (elem_is_protocol) {
|
||||
if (source_ty != elem_ty) val = self.buildProtocolErasure(val, arg, source_ty, elem_ty);
|
||||
}
|
||||
const ep = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = self.builder.constInt(@intCast(i), .s64) } }, self.module.types.ptrTo(elem_ty));
|
||||
const ep = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = self.builder.constInt(@intCast(i), .i64) } }, self.module.types.ptrTo(elem_ty));
|
||||
self.builder.store(ep, val);
|
||||
}
|
||||
const data_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = self.builder.constInt(0, .s64) } }, self.module.types.ptrTo(elem_ty));
|
||||
const data_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = self.builder.constInt(0, .i64) } }, self.module.types.ptrTo(elem_ty));
|
||||
self.builder.store(ptr_gep, data_ptr);
|
||||
self.builder.store(len_gep, self.builder.constInt(@intCast(arg_nodes.len), .s64));
|
||||
self.builder.store(len_gep, self.builder.constInt(@intCast(arg_nodes.len), .i64));
|
||||
return self.builder.load(slice_slot, slice_ty);
|
||||
}
|
||||
|
||||
@@ -211,12 +211,12 @@ pub fn lowerVariadicArgs(self: *Lowering, param_name: []const u8, call_args: []c
|
||||
if (n == 0) {
|
||||
// Empty slice: {null, 0}
|
||||
const null_ptr = self.builder.constNull(self.module.types.ptrTo(.any));
|
||||
const zero_len = self.builder.constInt(0, .s64);
|
||||
const zero_len = self.builder.constInt(0, .i64);
|
||||
const slice_slot = self.builder.alloca(any_slice_ty);
|
||||
// Store ptr (field 0) and len (field 1) into the slice alloca
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, self.module.types.ptrTo(.any), any_slice_ty);
|
||||
self.builder.store(ptr_gep, null_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, any_slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, any_slice_ty);
|
||||
self.builder.store(len_gep, zero_len);
|
||||
if (self.scope) |scope| {
|
||||
scope.put(param_name, .{ .ref = slice_slot, .ty = any_slice_ty, .is_alloca = true });
|
||||
@@ -232,7 +232,7 @@ pub fn lowerVariadicArgs(self: *Lowering, param_name: []const u8, call_args: []c
|
||||
for (call_args[start_idx..], 0..) |arg, i| {
|
||||
var val = self.lowerExpr(arg);
|
||||
var source_ty = self.inferExprType(arg);
|
||||
// If AST-based inference falls back to .s64 but the lowered ref is a string/struct, use that
|
||||
// If AST-based inference falls back to .i64 but the lowered ref is a string/struct, use that
|
||||
if (source_ty == .unresolved) {
|
||||
const ref_ty = self.builder.getRefType(val);
|
||||
if (ref_ty == .string or ref_ty == .f32 or ref_ty == .f64 or ref_ty == .bool) {
|
||||
@@ -270,7 +270,7 @@ pub fn lowerVariadicArgs(self: *Lowering, param_name: []const u8, call_args: []c
|
||||
}
|
||||
const boxed = if (source_ty == .any) val else self.builder.boxAny(val, source_ty);
|
||||
// GEP to array[i] and store
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .s64);
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .i64);
|
||||
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = idx_ref } }, self.module.types.ptrTo(.any));
|
||||
self.builder.store(elem_ptr, boxed);
|
||||
}
|
||||
@@ -278,13 +278,13 @@ pub fn lowerVariadicArgs(self: *Lowering, param_name: []const u8, call_args: []c
|
||||
// Build slice {ptr_to_first_element, len}
|
||||
const slice_slot = self.builder.alloca(any_slice_ty);
|
||||
// Get pointer to first element (array_slot is *[N x Any], GEP to element 0 gives *Any)
|
||||
const zero = self.builder.constInt(0, .s64);
|
||||
const zero = self.builder.constInt(0, .i64);
|
||||
const data_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = zero } }, self.module.types.ptrTo(.any));
|
||||
const len_ref = self.builder.constInt(@intCast(n), .s64);
|
||||
const len_ref = self.builder.constInt(@intCast(n), .i64);
|
||||
// Store into slice fields
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, self.module.types.ptrTo(.any), any_slice_ty);
|
||||
self.builder.store(ptr_gep, data_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, any_slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, any_slice_ty);
|
||||
self.builder.store(len_gep, len_ref);
|
||||
|
||||
if (self.scope) |scope| {
|
||||
@@ -351,11 +351,11 @@ pub fn packVariadicCallArgs(self: *Lowering, fd: *const ast.FnDecl, c: *const as
|
||||
if (variadic_count == 0) {
|
||||
// Empty slice
|
||||
const null_ptr = self.builder.constNull(self.module.types.ptrTo(elem_ty));
|
||||
const zero_len = self.builder.constInt(0, .s64);
|
||||
const zero_len = self.builder.constInt(0, .i64);
|
||||
const slice_slot = self.builder.alloca(slice_ty);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, self.module.types.ptrTo(elem_ty), slice_ty);
|
||||
self.builder.store(ptr_gep, null_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, slice_ty);
|
||||
self.builder.store(len_gep, zero_len);
|
||||
const slice_val = self.builder.load(slice_slot, slice_ty);
|
||||
// Replace args: keep fixed args, append slice
|
||||
@@ -385,7 +385,7 @@ pub fn packVariadicCallArgs(self: *Lowering, fd: *const ast.FnDecl, c: *const as
|
||||
var val = args.items[fixed_count + i];
|
||||
if (is_any) {
|
||||
var source_ty = self.inferExprType(c.args[fixed_count + i]);
|
||||
// If AST-based inference falls back to .s64 but the lowered ref has a richer type, use that
|
||||
// If AST-based inference falls back to .i64 but the lowered ref has a richer type, use that
|
||||
if (source_ty == .unresolved) {
|
||||
const ref_ty = self.builder.getRefType(val);
|
||||
if (ref_ty != .unresolved and ref_ty != .void) source_ty = ref_ty;
|
||||
@@ -432,19 +432,19 @@ pub fn packVariadicCallArgs(self: *Lowering, fd: *const ast.FnDecl, c: *const as
|
||||
val = self.buildProtocolErasure(val, arg_node, source_ty, elem_ty);
|
||||
}
|
||||
}
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .s64);
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .i64);
|
||||
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = idx_ref } }, self.module.types.ptrTo(array_elem));
|
||||
self.builder.store(elem_ptr, val);
|
||||
}
|
||||
|
||||
// Build slice {ptr, len}
|
||||
const slice_slot = self.builder.alloca(slice_ty);
|
||||
const zero = self.builder.constInt(0, .s64);
|
||||
const zero = self.builder.constInt(0, .i64);
|
||||
const data_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = zero } }, self.module.types.ptrTo(array_elem));
|
||||
const len_ref = self.builder.constInt(@intCast(variadic_count), .s64);
|
||||
const len_ref = self.builder.constInt(@intCast(variadic_count), .i64);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, self.module.types.ptrTo(array_elem), slice_ty);
|
||||
self.builder.store(ptr_gep, data_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, slice_ty);
|
||||
self.builder.store(len_gep, len_ref);
|
||||
const slice_val = self.builder.load(slice_slot, slice_ty);
|
||||
|
||||
@@ -476,11 +476,11 @@ pub fn buildPackSliceValue(self: *Lowering, arg_types: []const TypeId) Ref {
|
||||
|
||||
if (arg_types.len == 0) {
|
||||
const null_ptr = self.builder.constNull(any_ptr_ty);
|
||||
const zero_len = self.builder.constInt(0, .s64);
|
||||
const zero_len = self.builder.constInt(0, .i64);
|
||||
const slice_slot = self.builder.alloca(any_slice_ty);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, any_ptr_ty, any_slice_ty);
|
||||
self.builder.store(ptr_gep, null_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, any_slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, any_slice_ty);
|
||||
self.builder.store(len_gep, zero_len);
|
||||
return self.builder.load(slice_slot, any_slice_ty);
|
||||
}
|
||||
@@ -493,18 +493,18 @@ pub fn buildPackSliceValue(self: *Lowering, arg_types: []const TypeId) Ref {
|
||||
// (`{tag=.any, value=tid}`) — already the canonical Any
|
||||
// shape, so no re-box needed.
|
||||
const type_val = self.builder.constType(ty);
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .s64);
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .i64);
|
||||
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = idx_ref } }, any_ptr_ty);
|
||||
self.builder.store(elem_ptr, type_val);
|
||||
}
|
||||
|
||||
const slice_slot = self.builder.alloca(any_slice_ty);
|
||||
const zero = self.builder.constInt(0, .s64);
|
||||
const zero = self.builder.constInt(0, .i64);
|
||||
const data_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = zero } }, any_ptr_ty);
|
||||
const len_ref = self.builder.constInt(@intCast(arg_types.len), .s64);
|
||||
const len_ref = self.builder.constInt(@intCast(arg_types.len), .i64);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, any_ptr_ty, any_slice_ty);
|
||||
self.builder.store(ptr_gep, data_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, any_slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, any_slice_ty);
|
||||
self.builder.store(len_gep, len_ref);
|
||||
return self.builder.load(slice_slot, any_slice_ty);
|
||||
}
|
||||
@@ -521,11 +521,11 @@ pub fn materialisePackSlice(
|
||||
|
||||
if (arg_types.len == 0) {
|
||||
const null_ptr = self.builder.constNull(any_ptr_ty);
|
||||
const zero_len = self.builder.constInt(0, .s64);
|
||||
const zero_len = self.builder.constInt(0, .i64);
|
||||
const slice_slot = self.builder.alloca(any_slice_ty);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, any_ptr_ty, any_slice_ty);
|
||||
self.builder.store(ptr_gep, null_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, any_slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, any_slice_ty);
|
||||
self.builder.store(len_gep, zero_len);
|
||||
scope.put(pack_name, .{ .ref = slice_slot, .ty = any_slice_ty, .is_alloca = true });
|
||||
return;
|
||||
@@ -537,18 +537,18 @@ pub fn materialisePackSlice(
|
||||
for (slot_refs, arg_types, 0..) |slot, ty, i| {
|
||||
const val = self.builder.load(slot, ty);
|
||||
const boxed = if (ty == .any) val else self.builder.boxAny(val, ty);
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .s64);
|
||||
const idx_ref = self.builder.constInt(@intCast(i), .i64);
|
||||
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = idx_ref } }, any_ptr_ty);
|
||||
self.builder.store(elem_ptr, boxed);
|
||||
}
|
||||
|
||||
const slice_slot = self.builder.alloca(any_slice_ty);
|
||||
const zero = self.builder.constInt(0, .s64);
|
||||
const zero = self.builder.constInt(0, .i64);
|
||||
const data_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = zero } }, any_ptr_ty);
|
||||
const len_ref = self.builder.constInt(@intCast(arg_types.len), .s64);
|
||||
const len_ref = self.builder.constInt(@intCast(arg_types.len), .i64);
|
||||
const ptr_gep = self.builder.structGepTyped(slice_slot, 0, any_ptr_ty, any_slice_ty);
|
||||
self.builder.store(ptr_gep, data_ptr);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .s64, any_slice_ty);
|
||||
const len_gep = self.builder.structGepTyped(slice_slot, 1, .i64, any_slice_ty);
|
||||
self.builder.store(len_gep, len_ref);
|
||||
scope.put(pack_name, .{ .ref = slice_slot, .ty = any_slice_ty, .is_alloca = true });
|
||||
}
|
||||
@@ -558,8 +558,8 @@ pub fn materialisePackSlice(
|
||||
/// type: a `return X;` statement's value type, or — failing that —
|
||||
/// the tail expression of an arrow-form body. Caller must have
|
||||
/// `pack_arg_nodes` installed so `args[<lit>]` substitutes during
|
||||
/// inference. Falls back to `.s64` if nothing concrete is found
|
||||
/// (matches the broader "default to .s64" convention elsewhere).
|
||||
/// inference. Falls back to `.i64` if nothing concrete is found
|
||||
/// (matches the broader "default to .i64" convention elsewhere).
|
||||
pub fn inferPackBodyReturnType(self: *Lowering, body: *const Node) TypeId {
|
||||
// First try explicit `return X;` — walks past structured
|
||||
// control flow but stops at nested fn / lambda bodies.
|
||||
|
||||
@@ -67,10 +67,10 @@ pub fn registerProtocolDecl(self: *Lowering, pd: *const ast.ProtocolDecl) void {
|
||||
}
|
||||
|
||||
/// Instantiate a parameterized protocol as a runtime VALUE type:
|
||||
/// `VL(s64)` → a 16-byte `{ctx, __vtable}` protocol value (`is_protocol`),
|
||||
/// `VL(i64)` → a 16-byte `{ctx, __vtable}` protocol value (`is_protocol`),
|
||||
/// with method infos resolved under the type-arg binding (so `get -> T`
|
||||
/// becomes `get -> s64`) and the binding recorded for projection. Cached by
|
||||
/// the mangled name `VL__s64`. Mirrors the non-parameterized path in
|
||||
/// becomes `get -> i64`) and the binding recorded for projection. Cached by
|
||||
/// the mangled name `VL__i64`. Mirrors the non-parameterized path in
|
||||
/// `registerProtocolDecl`.
|
||||
pub fn instantiateParamProtocol(self: *Lowering, pd: *const ast.ProtocolDecl, args: []const *const Node) TypeId {
|
||||
const table = &self.module.types;
|
||||
@@ -105,7 +105,7 @@ pub fn instantiateParamProtocol(self: *Lowering, pd: *const ast.ProtocolDecl, ar
|
||||
const id = if (table.findByName(name_id)) |existing| existing else table.intern(struct_info);
|
||||
table.updatePreservingKey(id, struct_info);
|
||||
|
||||
// Method infos resolved with the type-arg binding (T → s64), pinned to
|
||||
// Method infos resolved with the type-arg binding (T → i64), pinned to
|
||||
// the protocol's OWN module (E4) so a method-signature type visible only
|
||||
// there resolves correctly when instantiated cross-module. `Self` and the
|
||||
// bound type-args short-circuit before the leaf; a concrete library type
|
||||
@@ -332,10 +332,10 @@ pub fn createProtocolThunk(self: *Lowering, proto_name: []const u8, concrete_typ
|
||||
if (self.program_index.fn_ast_map.contains(qualified)) {
|
||||
self.lazyLowerFunction(qualified);
|
||||
} else if (self.genericInstanceMethod(concrete_type_name, method.name)) |gm| {
|
||||
// Generic-struct instance (`Combined__s64_s64`): the impl method is
|
||||
// Generic-struct instance (`Combined__i64_i64`): the impl method is
|
||||
// authored on the instance's STAMPED decl (CP-4). Monomorphize it
|
||||
// for this instance's bindings so the thunk has a concrete
|
||||
// `Combined__s64_s64.get` to call.
|
||||
// `Combined__i64_i64.get` to call.
|
||||
self.monomorphizeFunction(gm.fd, qualified, gm.bindings);
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,7 @@ pub fn buildProtocolValue(self: *Lowering, concrete_ptr: Ref, proto_name: []cons
|
||||
var ctx_ptr = concrete_ptr;
|
||||
if (heap_copy) {
|
||||
const concrete_size = self.module.types.typeSizeBytes(concrete_ty);
|
||||
const size_ref = self.builder.constInt(@intCast(concrete_size), .s64);
|
||||
const size_ref = self.builder.constInt(@intCast(concrete_size), .i64);
|
||||
const heap_ptr = self.allocViaContext(size_ref, void_ptr_ty);
|
||||
_ = self.callForeign("memcpy", &.{ heap_ptr, concrete_ptr, size_ref }, void_ptr_ty);
|
||||
ctx_ptr = heap_ptr;
|
||||
@@ -591,7 +591,7 @@ pub fn emitProtocolDispatch(self: *Lowering, receiver: Ref, proto_info: Protocol
|
||||
/// Handles both direct types and pointer-to-types.
|
||||
pub fn resolveConcreteTypeName(self: *Lowering, ty: TypeId) ?[]const u8 {
|
||||
if (ty.isBuiltin()) {
|
||||
// Primitive types like s64 — check if they have toName()
|
||||
// Primitive types like i64 — check if they have toName()
|
||||
return self.module.types.typeName(ty);
|
||||
}
|
||||
const info = self.module.types.get(ty);
|
||||
|
||||
@@ -329,7 +329,7 @@ pub fn lowerVarDecl(self: *Lowering, vd: *const ast.VarDecl) void {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Coerce value to match target type (e.g. u8 → s64 widening)
|
||||
// Coerce value to match target type (e.g. u8 → i64 widening)
|
||||
{
|
||||
const ref_ty = self.builder.getRefType(ref);
|
||||
if (ref_ty != ty and ref_ty != .void and ty != .void) {
|
||||
@@ -354,7 +354,7 @@ pub fn lowerVarDecl(self: *Lowering, vd: *const ast.VarDecl) void {
|
||||
self.force_block_value = true;
|
||||
// An unannotated decl provides no target type: clear the ambient one
|
||||
// (the enclosing fn's implicit-return target) so literal initializers
|
||||
// take their spec defaults (s64/f64) instead of adopting it.
|
||||
// take their spec defaults (i64/f64) instead of adopting it.
|
||||
self.target_type = null;
|
||||
const ref = self.lowerExpr(val);
|
||||
self.force_block_value = saved_fbv;
|
||||
@@ -366,7 +366,7 @@ pub fn lowerVarDecl(self: *Lowering, vd: *const ast.VarDecl) void {
|
||||
scope.put(vd.name, .{ .ref = slot, .ty = ty, .is_alloca = true });
|
||||
}
|
||||
} else {
|
||||
const ty = TypeId.s64;
|
||||
const ty = TypeId.i64;
|
||||
const slot = self.builder.alloca(ty);
|
||||
self.builder.store(slot, self.zeroValue(ty));
|
||||
if (self.scope) |scope| {
|
||||
@@ -390,7 +390,7 @@ pub fn lowerLocalFnDecl(self: *Lowering, fd: *const ast.FnDecl) void {
|
||||
}
|
||||
|
||||
pub fn lowerConstDecl(self: *Lowering, cd: *const ast.ConstDecl) void {
|
||||
// Handle local function declarations: fx :: (s:s3) -> s3 { ... }
|
||||
// Handle local function declarations: fx :: (s:i3) -> i3 { ... }
|
||||
if (cd.value.data == .fn_decl) {
|
||||
const fd = &cd.value.data.fn_decl;
|
||||
// Use mangled name for local functions to support block-scoped shadowing
|
||||
@@ -448,15 +448,15 @@ pub fn lowerReturn(self: *Lowering, rs: *const ast.ReturnStmt) void {
|
||||
}
|
||||
// Set target_type to function return type so null_literal etc. get the right type.
|
||||
// When inlining a comptime body, the *inlined* fn's declared return type wins
|
||||
// over the caller's — otherwise `return 42` inside a `-> s64` body lowered into
|
||||
// a `-> s32` caller would coerce 42 to s32 before storing into the s64 slot.
|
||||
// over the caller's — otherwise `return 42` inside a `-> i64` body lowered into
|
||||
// a `-> i32` caller would coerce 42 to i32 before storing into the i64 slot.
|
||||
const old_target = self.target_type;
|
||||
const ret_ty_for_target: TypeId = if (self.inline_return_target) |iri|
|
||||
iri.ret_ty
|
||||
else if (self.builder.func) |fid|
|
||||
self.module.functions.items[@intFromEnum(fid)].ret
|
||||
else
|
||||
TypeId.s64;
|
||||
TypeId.i64;
|
||||
// A value-carrying failable (`-> (T..., !)`) returns its VALUE part and
|
||||
// the success error slot (0) is appended by lowerFailableSuccessReturn.
|
||||
// Resolve a BARE returned value against that value type, NOT the failable
|
||||
@@ -523,7 +523,7 @@ pub fn lowerReturn(self: *Lowering, rs: *const ast.ReturnStmt) void {
|
||||
const ret_ty = if (self.builder.func) |fid|
|
||||
self.module.functions.items[@intFromEnum(fid)].ret
|
||||
else
|
||||
TypeId.s64;
|
||||
TypeId.i64;
|
||||
if (ret_ty == .void) {
|
||||
// Void function — just return void (the value expression was evaluated for side effects)
|
||||
self.builder.retVoid();
|
||||
@@ -532,7 +532,7 @@ pub fn lowerReturn(self: *Lowering, rs: *const ast.ReturnStmt) void {
|
||||
// value part; the compiler appends the success error slot (0).
|
||||
self.lowerFailableSuccessReturn(ref, ret_ty, rs.value.?.span);
|
||||
} else {
|
||||
// Coerce return value to match function return type (e.g., ?s32 → s32)
|
||||
// Coerce return value to match function return type (e.g., ?i32 → i32)
|
||||
const val_ty = self.builder.getRefType(ref);
|
||||
const coerced = self.coerceToType(ref, val_ty, ret_ty);
|
||||
self.builder.ret(coerced, ret_ty);
|
||||
@@ -748,11 +748,11 @@ pub fn lowerAssignment(self: *Lowering, asgn: *const ast.Assignment) void {
|
||||
} else false);
|
||||
|
||||
if (is_special_container and std.mem.eql(u8, fa.field, "len")) {
|
||||
const gep = self.builder.structGepTyped(obj_ptr, 1, .s64, obj_ty);
|
||||
self.storeOrCompound(gep, val, asgn.op, .s64);
|
||||
const gep = self.builder.structGepTyped(obj_ptr, 1, .i64, obj_ty);
|
||||
self.storeOrCompound(gep, val, asgn.op, .i64);
|
||||
} else if (is_special_container and std.mem.eql(u8, fa.field, "ptr")) {
|
||||
const gep = self.builder.structGepTyped(obj_ptr, 0, .s64, obj_ty);
|
||||
self.storeOrCompound(gep, val, asgn.op, .s64);
|
||||
const gep = self.builder.structGepTyped(obj_ptr, 0, .i64, obj_ty);
|
||||
self.storeOrCompound(gep, val, asgn.op, .i64);
|
||||
} else if (self.fieldLvaluePtr(obj_ptr, obj_ty, fa.field)) |fl| {
|
||||
// Resolve the target field (struct / union direct / promoted
|
||||
// anonymous-struct member / tuple element / vector lane) via
|
||||
@@ -986,7 +986,7 @@ pub fn lowerExprAsPtr(self: *Lowering, node: *const Node) Ref {
|
||||
// resolver so address-of and the multi-target store path never
|
||||
// disagree on the slot. No match → emit the read path's
|
||||
// field-not-found diagnostic (lowerFieldAccessOnType →
|
||||
// emitFieldError) instead of silently GEPing field 0 as .s64;
|
||||
// emitFieldError) instead of silently GEPing field 0 as .i64;
|
||||
// that bogus pointer reaches LLVM emission as ptrTo(.unresolved)
|
||||
// and panics.
|
||||
if (self.fieldLvaluePtr(obj_ptr, obj_ty, fa.field)) |r| return r.ptr;
|
||||
|
||||
Reference in New Issue
Block a user