more forward declarations
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
#import "modules/std.sx";
|
||||
math :: #import "modules/math";
|
||||
|
||||
dot :: (a: Vector(3,f32), b: Vector(3,f32)) -> f32 {
|
||||
a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
cross :: (a: Vector(3,f32), b: Vector(3,f32)) -> Vector(3,f32) {
|
||||
.[a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x];
|
||||
}
|
||||
|
||||
length :: (v: Vector(3,f32)) -> f32 {
|
||||
math.sqrt(dot(v, v));
|
||||
}
|
||||
|
||||
normalize :: (v: Vector(3,f32)) -> Vector(3,f32) {
|
||||
v / length(v);
|
||||
}
|
||||
|
||||
vec3 :: (x:f32, y:f32, z:f32) -> Vector(3,f32) {
|
||||
.[x, y, z];
|
||||
}
|
||||
@@ -10,19 +26,19 @@ main :: () {
|
||||
b := vec3(0, 1, 0);
|
||||
|
||||
// dot product
|
||||
d := math.dot(a, b);
|
||||
d := dot(a, b);
|
||||
print("dot: {}\n", d);
|
||||
|
||||
// cross product
|
||||
cr := math.cross(a, b);
|
||||
cr := cross(a, b);
|
||||
print("cross: {}\n", cr);
|
||||
|
||||
// length
|
||||
v := vec3(3, 4, 0);
|
||||
len := math.length(v);
|
||||
len := length(v);
|
||||
print("length: {}\n", len);
|
||||
|
||||
// normalize
|
||||
n := math.normalize(v);
|
||||
n := normalize(v);
|
||||
print("norm: {}\n", n);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ sumOf10 :: () -> s32 {
|
||||
|
||||
someSum :: #run sumOf10();
|
||||
|
||||
main :: {
|
||||
main :: () {
|
||||
// Basic while loop: count to 5
|
||||
i := 0;
|
||||
while i < 5 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: {
|
||||
main :: () {
|
||||
i := 0;
|
||||
while i < 10 {
|
||||
i+=1;
|
||||
|
||||
@@ -31,6 +31,7 @@ main :: () {
|
||||
// 1. Basic closure with capture
|
||||
offset := 100;
|
||||
add_offset := closure((x: s64) -> s64 => x + offset);
|
||||
|
||||
print("basic: {}\n", add_offset(42));
|
||||
|
||||
// 2. Capture by value (snapshot semantics)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/math/math.sx";
|
||||
pkg :: #import "modules/testpkg";
|
||||
|
||||
// ============================================================
|
||||
@@ -277,7 +277,7 @@ SumBox :: struct ($T: Type/Summable) {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
main :: {
|
||||
main :: () {
|
||||
|
||||
// ========================================================
|
||||
// 1. LITERALS
|
||||
@@ -1977,7 +1977,6 @@ END;
|
||||
if x > 100 { return 100; }
|
||||
return x + offset;
|
||||
});
|
||||
// Workaround: assign result with explicit type so print wraps correctly
|
||||
r1 : s64 = clamp(10);
|
||||
r2 : s64 = clamp(0 - 5);
|
||||
r3 : s64 = clamp(999);
|
||||
|
||||
25
examples/issue-0002.sx
Normal file
25
examples/issue-0002.sx
Normal file
@@ -0,0 +1,25 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
// Issue: nested field assignment through pointer
|
||||
// self.inner.field = value should work when self is a pointer
|
||||
|
||||
Inner :: struct {
|
||||
len: s64;
|
||||
cap: s64;
|
||||
}
|
||||
|
||||
Outer :: struct {
|
||||
inner: Inner;
|
||||
count: s64;
|
||||
|
||||
reset :: (self: *Outer) {
|
||||
self.inner.len = 0; // error: field assignment target must be a variable
|
||||
self.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
main :: () {
|
||||
o := Outer.{ inner = Inner.{ len = 5, cap = 10 }, count = 0 };
|
||||
o.reset();
|
||||
print("{}\n", o.inner.len);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
// This file lives in modules/testpkg/ but imports modules/std.sx
|
||||
// via cwd-relative path (not relative to this file's directory).
|
||||
#import "modules/std.sx";
|
||||
// This file lives in modules/testpkg/ and imports std relative to its directory.
|
||||
#import "../std.sx";
|
||||
|
||||
cwd_greet :: () -> string { format("cwd-import-ok"); }
|
||||
|
||||
14
examples/test_union_array.sx
Normal file
14
examples/test_union_array.sx
Normal file
@@ -0,0 +1,14 @@
|
||||
#import "modules/std.sx";
|
||||
|
||||
Vec2 :: union {
|
||||
data: [2]f32;
|
||||
struct { x, y: f32; };
|
||||
}
|
||||
|
||||
main :: () {
|
||||
uv : Vec2 = ---;
|
||||
uv.x = 1.0;
|
||||
uv.y = 2.0;
|
||||
print("promoted-x: {}\n", uv.x);
|
||||
print("promoted-data0: {}\n", uv.data[0]);
|
||||
}
|
||||
8
specs.md
8
specs.md
@@ -951,9 +951,9 @@ main :: () {
|
||||
// void return, no -> annotation
|
||||
}
|
||||
|
||||
// Bare-block shorthand (equivalent to no-arg void function):
|
||||
main :: {
|
||||
// same as main :: () { ... }
|
||||
// No-arg void function:
|
||||
main :: () {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1468,7 +1468,7 @@ compute :: (x: s32) -> s32 {
|
||||
Bare blocks can be used as statements to introduce a new lexical scope. Variables declared inside a scope block are local to that block. No trailing `;` is required.
|
||||
|
||||
```sx
|
||||
main :: {
|
||||
main :: () {
|
||||
x := 42;
|
||||
{
|
||||
x := 6; // shadows outer x
|
||||
|
||||
176
src/codegen.zig
176
src/codegen.zig
@@ -201,6 +201,8 @@ pub const CodeGen = struct {
|
||||
current_match_tags: ?[]const u64 = null,
|
||||
// Functions deferred to compile after all types are registered (e.g. any_to_string)
|
||||
deferred_fn_bodies: std.ArrayList(DeferredFn),
|
||||
// AST nodes whose bodies were generated in Pass 4 (to avoid double generation in main)
|
||||
generated_bodies: std.AutoHashMap(*const Node, void),
|
||||
// Libraries to link against (from #library directives)
|
||||
foreign_libraries: std.ArrayList([]const u8),
|
||||
// Set of foreign function names (for ABI lowering at call sites)
|
||||
@@ -436,6 +438,7 @@ pub const CodeGen = struct {
|
||||
.any_type_id_map = std.StringHashMap(u64).init(allocator),
|
||||
.any_type_entries = std.StringHashMap(AnyTypeEntry).init(allocator),
|
||||
.deferred_fn_bodies = std.ArrayList(DeferredFn).empty,
|
||||
.generated_bodies = std.AutoHashMap(*const Node, void).init(allocator),
|
||||
.foreign_libraries = std.ArrayList([]const u8).empty,
|
||||
.foreign_fns = std.StringHashMap(void).init(allocator),
|
||||
.library_constants = std.StringHashMap([]const u8).init(allocator),
|
||||
@@ -1392,7 +1395,7 @@ pub const CodeGen = struct {
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| {
|
||||
if (fd.body.data != .builtin_expr and fd.type_params.len == 0) {
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
_ = try self.registerFnDecl(fd, fd.name);
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| try self.registerStructMethods(sd),
|
||||
@@ -1457,11 +1460,13 @@ pub const CodeGen = struct {
|
||||
} else {
|
||||
try self.genFnBody(fd, fd.name);
|
||||
}
|
||||
try self.generated_bodies.put(decl, {});
|
||||
}
|
||||
},
|
||||
.const_decl => |cd| {
|
||||
if (cd.value.data == .lambda) {
|
||||
try self.genLambdaBody(cd.name, cd.value.data.lambda);
|
||||
try self.generated_bodies.put(decl, {});
|
||||
}
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
@@ -2338,7 +2343,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void {
|
||||
fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !c.LLVMValueRef {
|
||||
const is_foreign = fd.body.data == .foreign_expr;
|
||||
// For foreign functions: resolve C symbol name (rename) and validate library ref
|
||||
const actual_llvm_name = if (is_foreign) blk: {
|
||||
@@ -2359,7 +2364,7 @@ pub const CodeGen = struct {
|
||||
} else llvm_name;
|
||||
const fn_type = try self.buildFnType(fd.params, fd.return_type, fd.name, is_foreign);
|
||||
const name_z = try self.allocator.dupeZ(u8, actual_llvm_name);
|
||||
_ = c.LLVMAddFunction(self.module, name_z.ptr, fn_type);
|
||||
const function = c.LLVMAddFunction(self.module, name_z.ptr, fn_type);
|
||||
// Track foreign functions for ABI lowering at call sites (use sx name for call-site lookup)
|
||||
if (is_foreign) {
|
||||
try self.foreign_fns.put(llvm_name, {});
|
||||
@@ -2394,6 +2399,7 @@ pub const CodeGen = struct {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
/// registerTypes helper: register type names within a namespace.
|
||||
@@ -2473,7 +2479,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
|
||||
if (fd.body.data == .foreign_expr) {
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
_ = try self.registerFnDecl(fd, fd.name);
|
||||
try self.foreign_fns.put(qualified, {});
|
||||
const fe = fd.body.data.foreign_expr;
|
||||
if (fe.c_name) |c_name| {
|
||||
@@ -2490,7 +2496,7 @@ pub const CodeGen = struct {
|
||||
} else if (fd.type_params.len > 0) {
|
||||
try self.generic_templates.put(qualified, fd);
|
||||
} else {
|
||||
try self.registerFnDecl(fd, qualified);
|
||||
_ = try self.registerFnDecl(fd, qualified);
|
||||
}
|
||||
},
|
||||
.struct_decl => |sd| {
|
||||
@@ -2980,8 +2986,10 @@ pub const CodeGen = struct {
|
||||
if (ret_val) |val| {
|
||||
const prepared = try self.prepareReturnValue(val, ret_sx_type);
|
||||
self.ret(prepared);
|
||||
} else {
|
||||
} else if (ret_sx_type == .void_type) {
|
||||
self.retVoid();
|
||||
} else {
|
||||
_ = c.LLVMBuildUnreachable(self.builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3014,30 +3022,45 @@ pub const CodeGen = struct {
|
||||
// Infer return type from body for => lambdas without explicit annotation
|
||||
const ret_sx_type = if (fd.return_type != null) self.resolveType(fd.return_type) else if (fd.is_arrow) self.inferType(fd.body) else Type.void_type;
|
||||
|
||||
// For arrow lambdas with inferred return type, build function manually
|
||||
if (fd.is_arrow and fd.return_type == null) {
|
||||
const ret_llvm_type = self.typeToLLVM(ret_sx_type);
|
||||
var param_llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
|
||||
for (fd.params) |param| {
|
||||
try param_llvm_types.append(self.allocator, self.typeToLLVM(self.resolveType(param.type_expr)));
|
||||
// Build or register the LLVM function, keeping a direct reference
|
||||
// (LLVMGetNamedFunction returns the first fn with that name, which
|
||||
// may differ when multiple local functions share a name)
|
||||
const function = blk: {
|
||||
if (fd.is_arrow and fd.return_type == null) {
|
||||
const ret_llvm_type = self.typeToLLVM(ret_sx_type);
|
||||
var param_llvm_types = std.ArrayList(c.LLVMTypeRef).empty;
|
||||
for (fd.params) |param| {
|
||||
try param_llvm_types.append(self.allocator, self.typeToLLVM(self.resolveType(param.type_expr)));
|
||||
}
|
||||
const params_slice = try param_llvm_types.toOwnedSlice(self.allocator);
|
||||
const fn_type = c.LLVMFunctionType(
|
||||
ret_llvm_type,
|
||||
if (params_slice.len > 0) params_slice.ptr else null,
|
||||
@intCast(params_slice.len),
|
||||
0,
|
||||
);
|
||||
const name_z2 = try self.allocator.dupeZ(u8, fd.name);
|
||||
const func = c.LLVMAddFunction(self.module, name_z2.ptr, fn_type);
|
||||
try self.function_return_types.put(fd.name, ret_sx_type);
|
||||
break :blk func;
|
||||
} else {
|
||||
break :blk try self.registerFnDecl(fd, fd.name);
|
||||
}
|
||||
const params_slice = try param_llvm_types.toOwnedSlice(self.allocator);
|
||||
const fn_type = c.LLVMFunctionType(
|
||||
ret_llvm_type,
|
||||
if (params_slice.len > 0) params_slice.ptr else null,
|
||||
@intCast(params_slice.len),
|
||||
0,
|
||||
);
|
||||
const name_z2 = try self.allocator.dupeZ(u8, fd.name);
|
||||
_ = c.LLVMAddFunction(self.module, name_z2.ptr, fn_type);
|
||||
try self.function_return_types.put(fd.name, ret_sx_type);
|
||||
} else {
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
};
|
||||
|
||||
// Skip if this exact AST node was already generated in Pass 4
|
||||
// (top-level fn_decls appear both in Pass 4 and main's body)
|
||||
if (self.generated_bodies.contains(node)) {
|
||||
self.named_values.deinit();
|
||||
self.named_values = saved_named;
|
||||
self.narrowed_types = saved_narrowed;
|
||||
self.current_return_type = saved_ret;
|
||||
self.current_function = saved_fn;
|
||||
self.positionAt(saved_bb);
|
||||
return null;
|
||||
}
|
||||
|
||||
self.current_return_type = ret_sx_type;
|
||||
const name_z = try self.allocator.dupeZ(u8, fd.name);
|
||||
const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse
|
||||
return self.emitErrorFmt("local function '{s}' not found", .{fd.name});
|
||||
self.current_function = function;
|
||||
_ = self.appendBlock(function, "entry");
|
||||
|
||||
@@ -3071,7 +3094,7 @@ pub const CodeGen = struct {
|
||||
const ret_val = try self.prepareReturnValue(val, ret_sx_type);
|
||||
self.ret(ret_val);
|
||||
} else {
|
||||
self.retVoid();
|
||||
_ = c.LLVMBuildUnreachable(self.builder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3081,6 +3104,25 @@ pub const CodeGen = struct {
|
||||
self.current_return_type = saved_ret;
|
||||
self.current_function = saved_fn;
|
||||
self.positionAt(saved_bb);
|
||||
|
||||
// Register local function in outer scope's named_values so it
|
||||
// shadows any top-level function with the same name.
|
||||
{
|
||||
var param_types_list = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
try param_types_list.append(self.allocator, self.resolveType(param.type_expr));
|
||||
}
|
||||
const ret_type_ptr = try self.allocator.create(Type);
|
||||
ret_type_ptr.* = ret_sx_type;
|
||||
const fn_ty: Type = .{ .function_type = .{
|
||||
.param_types = try param_types_list.toOwnedSlice(self.allocator),
|
||||
.return_type = ret_type_ptr,
|
||||
} };
|
||||
const local_name_z = try self.allocator.dupeZ(u8, fd.name);
|
||||
const fn_alloca = self.buildEntryBlockAlloca(self.ptrType(), local_name_z.ptr);
|
||||
_ = c.LLVMBuildStore(self.builder, function, fn_alloca);
|
||||
try self.named_values.put(fd.name, .{ .ptr = fn_alloca, .ty = fn_ty });
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -4841,7 +4883,7 @@ pub const CodeGen = struct {
|
||||
try self.generic_templates.put(qualified, fd);
|
||||
} else {
|
||||
// Non-generic struct, non-generic method: register directly
|
||||
try self.registerFnDecl(fd, qualified);
|
||||
_ = try self.registerFnDecl(fd, qualified);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4851,6 +4893,9 @@ pub const CodeGen = struct {
|
||||
fn registerProtocolDecl(self: *CodeGen, pd: ast.ProtocolDecl) !void {
|
||||
try self.protocol_decls.put(pd.name, pd);
|
||||
|
||||
// Skip if already registered (can happen with diamond imports)
|
||||
if (self.type_registry.contains(pd.name)) return;
|
||||
|
||||
if (pd.is_inline) {
|
||||
// #inline protocol: generate struct { ctx: *void, method1: fn_ptr, method2: fn_ptr, ... }
|
||||
const n_fields = 1 + pd.methods.len; // ctx + one fn-ptr per method
|
||||
@@ -5188,7 +5233,7 @@ pub const CodeGen = struct {
|
||||
try self.generic_templates.put(qualified, fd);
|
||||
} else {
|
||||
// Non-generic: register directly
|
||||
try self.registerFnDecl(fd, qualified);
|
||||
_ = try self.registerFnDecl(fd, qualified);
|
||||
}
|
||||
try self.fn_signatures.put(qualified, self.buildFnSignature(fd));
|
||||
}
|
||||
@@ -5211,7 +5256,7 @@ pub const CodeGen = struct {
|
||||
// Synthesize a fn_decl: method_name :: (self: *ConcreteType, params...) -> R { default_body }
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ib.target_type, method.name });
|
||||
const self_fd = try self.synthesizeDefaultMethod(ib.target_type, method);
|
||||
try self.registerFnDecl(self_fd, qualified);
|
||||
_ = try self.registerFnDecl(self_fd, qualified);
|
||||
try self.fn_signatures.put(qualified, self.buildFnSignature(self_fd));
|
||||
}
|
||||
}
|
||||
@@ -7931,30 +7976,6 @@ pub const CodeGen = struct {
|
||||
return self.genCallByName(resolved, call_node);
|
||||
}
|
||||
|
||||
// Check if this is a generic function call
|
||||
if (self.generic_templates.get(callee_name)) |template| {
|
||||
return self.genGenericCall(callee_name, template, call_node);
|
||||
}
|
||||
// Intra-namespace fallback for generic templates
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name });
|
||||
if (self.generic_templates.get(qualified)) |template| {
|
||||
return self.genGenericCall(qualified, template, call_node);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for #builtin function (only available when imported)
|
||||
if (self.builtin_functions.contains(callee_name)) {
|
||||
return self.dispatchBuiltin(callee_name, call_node);
|
||||
}
|
||||
// Intra-namespace fallback for builtins
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified_builtin = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name });
|
||||
if (self.builtin_functions.contains(qualified_builtin)) {
|
||||
return self.dispatchBuiltin(qualified_builtin, call_node);
|
||||
}
|
||||
}
|
||||
|
||||
// Compiler intrinsics (always available, no #builtin declaration needed)
|
||||
if (std.mem.eql(u8, callee_name, "sqrt")) {
|
||||
return self.genMathIntrinsic(call_node, "sqrt");
|
||||
@@ -7981,7 +8002,8 @@ pub const CodeGen = struct {
|
||||
return self.genClosureIntrinsic(call_node);
|
||||
}
|
||||
|
||||
// Local variable takes priority: closures and function pointers shadow LLVM named functions
|
||||
// Local variables shadow imported functions: closures and function pointers
|
||||
// take priority over generic templates, builtins, and LLVM named functions.
|
||||
if (self.lookupValue(callee_name)) |v| {
|
||||
const entry = v.asNamedValue();
|
||||
if (entry) |e| {
|
||||
@@ -7994,6 +8016,30 @@ pub const CodeGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a generic function call
|
||||
if (self.generic_templates.get(callee_name)) |template| {
|
||||
return self.genGenericCall(callee_name, template, call_node);
|
||||
}
|
||||
// Intra-namespace fallback for generic templates
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name });
|
||||
if (self.generic_templates.get(qualified)) |template| {
|
||||
return self.genGenericCall(qualified, template, call_node);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for #builtin function (only available when imported)
|
||||
if (self.builtin_functions.contains(callee_name)) {
|
||||
return self.dispatchBuiltin(callee_name, call_node);
|
||||
}
|
||||
// Intra-namespace fallback for builtins
|
||||
if (self.current_namespace) |ns| {
|
||||
const qualified_builtin = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns, callee_name });
|
||||
if (self.builtin_functions.contains(qualified_builtin)) {
|
||||
return self.dispatchBuiltin(qualified_builtin, call_node);
|
||||
}
|
||||
}
|
||||
|
||||
var nbuf: [256]u8 = undefined;
|
||||
var callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(callee_name, &nbuf));
|
||||
// Foreign function fallback: qualified name "ns.Func" → try unqualified "Func" (the C symbol)
|
||||
@@ -8417,7 +8463,23 @@ pub const CodeGen = struct {
|
||||
if (ret_llvm == self.voidType()) {
|
||||
_ = c.LLVMBuildRetVoid(self.builder);
|
||||
} else {
|
||||
_ = c.LLVMBuildRet(self.builder, result);
|
||||
// Convert result type if it doesn't match the thunk's declared return type
|
||||
var ret_val = result;
|
||||
const result_ty = c.LLVMTypeOf(result);
|
||||
if (result_ty != ret_llvm) {
|
||||
const src_kind = c.LLVMGetTypeKind(result_ty);
|
||||
const dst_kind = c.LLVMGetTypeKind(ret_llvm);
|
||||
if (src_kind == c.LLVMIntegerTypeKind and dst_kind == c.LLVMIntegerTypeKind) {
|
||||
const src_bits = c.LLVMGetIntTypeWidth(result_ty);
|
||||
const dst_bits = c.LLVMGetIntTypeWidth(ret_llvm);
|
||||
if (src_bits > dst_bits) {
|
||||
ret_val = c.LLVMBuildTrunc(self.builder, result, ret_llvm, "thunk_trunc");
|
||||
} else {
|
||||
ret_val = c.LLVMBuildSExt(self.builder, result, ret_llvm, "thunk_sext");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = c.LLVMBuildRet(self.builder, ret_val);
|
||||
}
|
||||
|
||||
// Restore position
|
||||
|
||||
@@ -1084,7 +1084,7 @@ pub const Server = struct {
|
||||
};
|
||||
|
||||
var hints = std.ArrayList(lsp.InlayHint).empty;
|
||||
collectInlayHints(self.allocator, root, sema.symbols, doc.source, &hints);
|
||||
collectInlayHints(self.allocator, root, sema.symbols, sema.fn_signatures, doc.source, &hints);
|
||||
self.collectCallHints(doc, root, &hints);
|
||||
const result_json = try lsp.inlayHintsJson(self.allocator, hints.items);
|
||||
try self.sendResponse(id_json, result_json);
|
||||
@@ -1094,37 +1094,46 @@ pub const Server = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
node: *const sx.ast.Node,
|
||||
symbols: []const sx.sema.Symbol,
|
||||
fn_signatures: std.StringHashMap(sx.sema.FnSignature),
|
||||
source: [:0]const u8,
|
||||
hints: *std.ArrayList(lsp.InlayHint),
|
||||
) void {
|
||||
switch (node.data) {
|
||||
.root => |r| {
|
||||
for (r.decls) |decl| collectInlayHints(allocator, decl, symbols, source, hints);
|
||||
for (r.decls) |decl| collectInlayHints(allocator, decl, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.block => |b| {
|
||||
for (b.stmts) |stmt| collectInlayHints(allocator, stmt, symbols, source, hints);
|
||||
for (b.stmts) |stmt| collectInlayHints(allocator, stmt, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.fn_decl => |fd| {
|
||||
collectInlayHints(allocator, fd.body, symbols, source, hints);
|
||||
collectInlayHints(allocator, fd.body, symbols, fn_signatures, source, hints);
|
||||
// Return type hint for arrow functions without explicit return type
|
||||
if (fd.return_type == null and fd.is_arrow) {
|
||||
if (fn_signatures.get(fd.name)) |sig| {
|
||||
if (sig.return_type != .void_type) {
|
||||
addReturnTypeHint(allocator, node.span, source, sig.return_type, hints);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
.lambda => |lm| {
|
||||
collectInlayHints(allocator, lm.body, symbols, source, hints);
|
||||
collectInlayHints(allocator, lm.body, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
if (ie.binding_name) |bname| {
|
||||
addBindingHint(allocator, bname, node.span, symbols, source, hints);
|
||||
}
|
||||
collectInlayHints(allocator, ie.then_branch, symbols, source, hints);
|
||||
if (ie.else_branch) |eb| collectInlayHints(allocator, eb, symbols, source, hints);
|
||||
collectInlayHints(allocator, ie.then_branch, symbols, fn_signatures, source, hints);
|
||||
if (ie.else_branch) |eb| collectInlayHints(allocator, eb, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.while_expr => |we| {
|
||||
if (we.binding_name) |bname| {
|
||||
addBindingHint(allocator, bname, node.span, symbols, source, hints);
|
||||
}
|
||||
collectInlayHints(allocator, we.body, symbols, source, hints);
|
||||
collectInlayHints(allocator, we.body, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.for_expr => |fe| {
|
||||
collectInlayHints(allocator, fe.body, symbols, source, hints);
|
||||
collectInlayHints(allocator, fe.body, symbols, fn_signatures, source, hints);
|
||||
},
|
||||
.var_decl => |vd| {
|
||||
// Only show hint when type is inferred (:= syntax)
|
||||
@@ -1135,9 +1144,22 @@ pub const Server = struct {
|
||||
.const_decl => |cd| {
|
||||
// Skip if explicit type annotation
|
||||
if (cd.type_annotation != null) return;
|
||||
// Handle lambda with return type hint
|
||||
if (cd.value.data == .lambda) {
|
||||
const lam = cd.value.data.lambda;
|
||||
collectInlayHints(allocator, lam.body, symbols, fn_signatures, source, hints);
|
||||
if (lam.return_type == null) {
|
||||
if (fn_signatures.get(cd.name)) |sig| {
|
||||
if (sig.return_type != .void_type) {
|
||||
addReturnTypeHint(allocator, cd.value.span, source, sig.return_type, hints);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Skip functions, types, structs, enums, unions, comptime, foreign, library
|
||||
switch (cd.value.data) {
|
||||
.lambda, .fn_decl, .type_expr, .struct_decl, .enum_decl, .union_decl,
|
||||
.fn_decl, .type_expr, .struct_decl, .enum_decl, .union_decl,
|
||||
.comptime_expr, .foreign_expr, .library_decl,
|
||||
=> return,
|
||||
else => {},
|
||||
@@ -1241,6 +1263,47 @@ pub const Server = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn addReturnTypeHint(
|
||||
allocator: std.mem.Allocator,
|
||||
span: sx.ast.Span,
|
||||
source: [:0]const u8,
|
||||
return_type: sx.types.Type,
|
||||
hints: *std.ArrayList(lsp.InlayHint),
|
||||
) void {
|
||||
// Find '(' from span start
|
||||
var pos: u32 = span.start;
|
||||
while (pos < source.len and source[pos] != '(') : (pos += 1) {}
|
||||
if (pos >= source.len) return;
|
||||
|
||||
// Match nested parens to find closing ')'
|
||||
var depth: u32 = 0;
|
||||
while (pos < source.len) : (pos += 1) {
|
||||
if (source[pos] == '(') {
|
||||
depth += 1;
|
||||
} else if (source[pos] == ')') {
|
||||
depth -= 1;
|
||||
if (depth == 0) break;
|
||||
}
|
||||
}
|
||||
if (pos >= source.len or depth != 0) return;
|
||||
|
||||
// Place hint right after ')'
|
||||
const loc = sx.errors.SourceLoc.compute(source, pos + 1);
|
||||
if (loc.line == 0 or loc.col == 0) return;
|
||||
|
||||
const type_name = return_type.displayName(allocator) catch return;
|
||||
const label = std.fmt.allocPrint(allocator, "-> {s}", .{type_name}) catch return;
|
||||
|
||||
hints.append(allocator, .{
|
||||
.line = loc.line - 1,
|
||||
.character = loc.col - 1,
|
||||
.label = label,
|
||||
.kind = 1,
|
||||
.padding_left = true,
|
||||
.padding_right = true,
|
||||
}) catch {};
|
||||
}
|
||||
|
||||
fn findSymbolAtSpan(symbols: []const sx.sema.Symbol, span_start: u32, name: []const u8) ?sx.sema.Symbol {
|
||||
for (symbols) |sym| {
|
||||
if (sym.def_span.start == span_start and std.mem.eql(u8, sym.name, name)) {
|
||||
|
||||
44
src/sema.zig
44
src/sema.zig
@@ -129,7 +129,8 @@ pub const Analyzer = struct {
|
||||
fn registerTopLevelDeclPrefixed(self: *Analyzer, node: *Node, ns_prefix: ?[]const u8) !void {
|
||||
switch (node.data) {
|
||||
.fn_decl => |fd| {
|
||||
const ret_ty = resolveReturnType(fd);
|
||||
const ret_ty = resolveReturnType(fd) orelse
|
||||
if (fd.is_arrow) self.inferFnReturnType(fd.params, fd.body) else null;
|
||||
try self.addSymbol(fd.name, .function, ret_ty, node.span);
|
||||
// Populate fn_signatures registry
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
@@ -171,7 +172,10 @@ pub const Analyzer = struct {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
try param_types.append(self.allocator, pt);
|
||||
}
|
||||
const ret = if (lam.return_type) |rt| Type.fromTypeExpr(rt) orelse .void_type else .void_type;
|
||||
const ret = if (lam.return_type) |rt|
|
||||
Type.fromTypeExpr(rt) orelse .void_type
|
||||
else
|
||||
self.inferFnReturnType(lam.params, lam.body) orelse .void_type;
|
||||
const key = if (ns_prefix) |pfx|
|
||||
try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ pfx, cd.name })
|
||||
else
|
||||
@@ -666,7 +670,21 @@ pub const Analyzer = struct {
|
||||
fn analyzeNode(self: *Analyzer, node: *Node) !void {
|
||||
switch (node.data) {
|
||||
.fn_decl => |fd| {
|
||||
try self.addSymbol(fd.name, .function, resolveReturnType(fd), node.span);
|
||||
const local_ret_ty = resolveReturnType(fd) orelse
|
||||
if (fd.is_arrow) self.inferFnReturnType(fd.params, fd.body) else null;
|
||||
try self.addSymbol(fd.name, .function, local_ret_ty, node.span);
|
||||
// Register fn_signatures for local functions (for return type hints + hover)
|
||||
{
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
try param_types.append(self.allocator, pt);
|
||||
}
|
||||
try self.fn_signatures.put(fd.name, .{
|
||||
.param_types = try param_types.toOwnedSlice(self.allocator),
|
||||
.return_type = local_ret_ty orelse .void_type,
|
||||
});
|
||||
}
|
||||
try self.pushScope();
|
||||
try self.analyzeParams(fd.params);
|
||||
try self.analyzeNode(fd.body);
|
||||
@@ -967,6 +985,26 @@ pub const Analyzer = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Infer return type from a function/lambda body by temporarily registering params.
|
||||
fn inferFnReturnType(self: *Analyzer, params: []const ast.Param, body: *const Node) ?Type {
|
||||
self.pushScope() catch return null;
|
||||
for (params) |param| {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
self.addSymbol(param.name, .param, pt, param.name_span) catch {};
|
||||
}
|
||||
// Arrow fn_decl wraps body in block{[expr]} — unwrap to inner expression
|
||||
const expr_node = if (body.data == .block) blk: {
|
||||
const stmts = body.data.block.stmts;
|
||||
if (stmts.len > 0) break :blk stmts[stmts.len - 1];
|
||||
break :blk body;
|
||||
} else body;
|
||||
|
||||
const inferred = self.inferExprType(expr_node);
|
||||
self.popScope();
|
||||
if (inferred != .void_type) return inferred;
|
||||
return null;
|
||||
}
|
||||
|
||||
fn resolveTypeAnnotation(self: *Analyzer, type_node: ?*Node) ?Type {
|
||||
if (type_node) |tn| {
|
||||
if (Type.fromTypeExpr(tn)) |t| return t;
|
||||
|
||||
@@ -1,2 +1,564 @@
|
||||
/Volumes/Store/dev/swipelab/sx/examples/50-smoke.sx:5:48: error: cannot read import 'modules/std.sx' (not a file or directory)
|
||||
/Volumes/Store/dev/swipelab/sx/examples/50-smoke.sx:3:1: error: cannot read import '/Volumes/Store/dev/swipelab/sx/examples/modules/testpkg' (not a file or directory)
|
||||
=== 1. Literals ===
|
||||
decimal: 42
|
||||
hex: 255
|
||||
binary: 10
|
||||
float: 3.140000
|
||||
f64: 2.718281
|
||||
true: true
|
||||
false: false
|
||||
escapes: hello world
|
||||
multiline: line1
|
||||
line2
|
||||
heredoc: raw heredoc
|
||||
|
||||
undef-then-set: 77
|
||||
enum-lit: .green
|
||||
null-ptr: null
|
||||
string-len: 5
|
||||
empty-string: 0
|
||||
=== 2. Operators ===
|
||||
add: 7
|
||||
sub: 7
|
||||
mul: 42
|
||||
div: 5
|
||||
mod: 2
|
||||
neg: -5
|
||||
eq: true
|
||||
neq: true
|
||||
lt: true
|
||||
gt: true
|
||||
le: true
|
||||
ge: true
|
||||
chain: true
|
||||
chain-gt: true
|
||||
chain-mixed: true
|
||||
eq-chain: true
|
||||
eq-chain-f: false
|
||||
band: 15
|
||||
bor: 7
|
||||
bxor: 240
|
||||
bxor2: 5
|
||||
bnot: -1
|
||||
bnot2: -2
|
||||
shl: 16
|
||||
shr: 16
|
||||
shl2: 24
|
||||
shr2: 127
|
||||
band-var: 15
|
||||
bor-var: 7
|
||||
bxor-var: 240
|
||||
shl-var: 16
|
||||
shr-var: 15
|
||||
bnot-var: -16
|
||||
and-assign: 15
|
||||
or-assign: 255
|
||||
xor-assign: 240
|
||||
shl-assign: 256
|
||||
shr-assign: 16
|
||||
mod-var: 2
|
||||
and: true
|
||||
and-false: false
|
||||
or: true
|
||||
or-false: false
|
||||
short-and: false
|
||||
short-or: true
|
||||
ca+=: 15
|
||||
ca-=: 12
|
||||
ca*=: 24
|
||||
ca/=: 4
|
||||
prec1: 14
|
||||
prec2: 20
|
||||
xx-cast: 200
|
||||
widen-u8-s64: 200
|
||||
widen-s32-f64: 42.000000
|
||||
widen-f32-f64: 1.500000
|
||||
widen-u8-s16: 100
|
||||
xx-s64-s32: 12345
|
||||
xx-f64-f32: 1.500000
|
||||
xx-f64-s32: 7
|
||||
=== 3. Types ===
|
||||
s8: 127
|
||||
s16: 32000
|
||||
s32: 100000
|
||||
u8: 255
|
||||
u16: 65000
|
||||
u32: 4000000
|
||||
alias: 1.500000
|
||||
struct-pos: Point{x: 1, y: 2}
|
||||
struct-prefix: Point{x: 3, y: 4}
|
||||
struct-named: Point{x: 20, y: 10}
|
||||
struct-shorthand: Point{x: 5, y: 6}
|
||||
defaults: a=0 b=99
|
||||
field-assign: Point{x: 42, y: 99}
|
||||
enum: .red
|
||||
enum-eq: true
|
||||
enum-neq: true
|
||||
backing: .err
|
||||
tagged: .circle(3.140000)
|
||||
payload: 3.140000
|
||||
void-variant: .none
|
||||
reassign: .circle(1.000000)
|
||||
reassign2: .rect(Shape.rect{w: 5.000000, h: 3.000000})
|
||||
enum-prefix: .circle(2.500000)
|
||||
match: rect
|
||||
match-expr: 10
|
||||
match-expr-else: 99
|
||||
capture: 9.500000
|
||||
capture-arrow: 7.500000
|
||||
else-match: other
|
||||
int-match: two
|
||||
int-match-else: unknown
|
||||
bool-match-t: yes
|
||||
bool-match-f: no
|
||||
bool: true
|
||||
union-f: 3.140000
|
||||
union-i: 1078523331
|
||||
promoted-x: 1.000000
|
||||
promoted-data0: 1.000000
|
||||
arr[2]: 30
|
||||
arr.len: 5
|
||||
arr-assign: [1, 99, 3]
|
||||
sl[0]: 1
|
||||
sl.len: 5
|
||||
sl-assign: [10, 55, 0]
|
||||
sub: [20, 30, 40]
|
||||
head: [10, 20, 30]
|
||||
tail: [30, 40, 50]
|
||||
slice-of-slice: [20, 30]
|
||||
strsub: world
|
||||
str-prefix: hello
|
||||
str-suffix: world
|
||||
deref: Point{x: 10, y: 20}
|
||||
auto-deref: 10
|
||||
mp[0]: 10
|
||||
mp[3]: 40
|
||||
mp-write: 99
|
||||
ptr==null: true
|
||||
ptr!=null: false
|
||||
ptr2==null: false
|
||||
ptr2!=null: true
|
||||
vec-construct: [1.000000, 3.000000, 2.000000]
|
||||
vec-add: [5.000000, 7.000000, 9.000000]
|
||||
vec-sub: [4.000000, 3.000000, 2.000000]
|
||||
vec-mul: [2.000000, 6.000000, 12.000000]
|
||||
vec-div: [5.000000, 3.000000, 2.000000]
|
||||
vec-scalar: [2.000000, 6.000000, 4.000000]
|
||||
vec-neg: [-1.000000, -3.000000, -2.000000]
|
||||
vec-x: 10.000000
|
||||
vec-y: 20.000000
|
||||
vec-z: 30.000000
|
||||
vec-idx: 20.000000
|
||||
=== 4. Control Flow ===
|
||||
ite: 1
|
||||
ite-both: 10 20
|
||||
if-block: yes
|
||||
if-no-else: after
|
||||
nested-if: deep
|
||||
if-else-if: second
|
||||
if-block-expr: 15
|
||||
while: 5
|
||||
while-false: skipped
|
||||
while-break: 7
|
||||
while-continue: 25
|
||||
while-sum: 55
|
||||
nested-while: 9
|
||||
nested-break: 2 2
|
||||
for: 10 20 30 40
|
||||
for-print: 10 20 30 40
|
||||
for-idx: 0 1 2 3
|
||||
for-2arg: 10@0 20@1 30@2 40@3
|
||||
for-break: 10 20
|
||||
for-continue: 10 30 40
|
||||
for-slice: 10 20 30
|
||||
for-slice-idx: 0:10 1:20 2:30
|
||||
for-nested: (0,0) (0,1) (1,0) (1,1)
|
||||
for-break-idx: 2
|
||||
multi: 1 2 3
|
||||
=== 5. Functions ===
|
||||
const: 42
|
||||
typed-const: 3.140000
|
||||
default-init: 0
|
||||
implicit-ret: 42
|
||||
early-ret: 5
|
||||
early-ret2: 99
|
||||
void-return: ok
|
||||
generic-s32: 42
|
||||
generic-f32: 1.500000
|
||||
generic-bool: true
|
||||
generic-multi: 30
|
||||
lambda: 14
|
||||
lambda-ret: 5.000000
|
||||
local-fn: 7
|
||||
fn-nested: 26
|
||||
varargs: 15
|
||||
spread: 60
|
||||
fp: 7
|
||||
fp-reassign: 12
|
||||
fp-apply: 30
|
||||
=== 6. Scoping ===
|
||||
inner: 200
|
||||
outer: 100
|
||||
shadow-type: 42
|
||||
shadow-type: 3.140000
|
||||
nest3: 3
|
||||
nest2: 2
|
||||
nest1: 1
|
||||
scope-isolate: 100
|
||||
scope-reuse: 1
|
||||
scope-reuse: 2
|
||||
scope-reuse: 1
|
||||
defer-a
|
||||
defer-b
|
||||
defer-c
|
||||
d4
|
||||
d3
|
||||
d2
|
||||
d1
|
||||
inner-defer
|
||||
outer-defer
|
||||
defer-in-if: body
|
||||
defer-in-if: deferred
|
||||
=== 7. Builtins ===
|
||||
out-ok
|
||||
sqrt: 3.000000
|
||||
sqrt-f64: 4.000000
|
||||
sizeof-s32: 4
|
||||
sizeof-f64: 8
|
||||
sizeof-struct: 8
|
||||
typeof: int
|
||||
typeof-float: float
|
||||
typeof-string: string
|
||||
typeof-bool: bool
|
||||
typeof-struct: struct
|
||||
typeof-enum: enum
|
||||
typename: Point
|
||||
fieldcount: 2
|
||||
fieldcount-enum: 3
|
||||
fieldname0: x
|
||||
fieldname1: y
|
||||
fieldname-enum0: red
|
||||
fieldname-enum2: blue
|
||||
fieldval0: 11
|
||||
fieldval1: 22
|
||||
fieldidx: 1
|
||||
fieldidx-tagged: 0
|
||||
fieldidx-tagged2: 2
|
||||
cast: 3
|
||||
cast-int-f64: 42.000000
|
||||
=== 8. Comptime ===
|
||||
run-const: 25
|
||||
run-expr: 42
|
||||
run-chain: 30
|
||||
ct-opt-coalesce: 141
|
||||
ct-opt-unwrap: 77
|
||||
ct-opt-guard: 10
|
||||
insert-ok
|
||||
insert-gen: 42
|
||||
=== 9. Flags ===
|
||||
flags: .read | .write
|
||||
has-read: yes
|
||||
flags-neg: no-read
|
||||
flags-single: .execute
|
||||
flags-all: .read | .write | .execute
|
||||
flags-raw: 3
|
||||
flags-explicit: .vsync | .resizable
|
||||
flags-explicit-raw: 68
|
||||
--- swap ---
|
||||
var swap: 20 10
|
||||
arr swap: 3 1
|
||||
3-way: 3 1 2
|
||||
=== 15. Foreign ===
|
||||
foreign-rename: 42
|
||||
=== 16. Compound Assign ===
|
||||
f64+=f32: 13.000000
|
||||
s64-=s32: 93
|
||||
=== 17. Slice Ptr ===
|
||||
sl-ptr[0]: 20
|
||||
sl-ptr[1]: 30
|
||||
=== 18. Array of Structs ===
|
||||
arr-struct-x: 3
|
||||
for-struct: Point{x: 1, y: 2}
|
||||
for-struct: Point{x: 3, y: 4}
|
||||
=== 19. Local Fn Return ===
|
||||
local-struct: 42 99
|
||||
local-enum: .circle(2.500000)
|
||||
=== 20. UFCS Return Type ===
|
||||
direct: 7
|
||||
ufcs: 7
|
||||
=== 21. Type-Named Vars ===
|
||||
s2: 42
|
||||
s2+1: 43
|
||||
=== 22. If-Struct ===
|
||||
if-struct: 10 20
|
||||
else-struct: 30 40
|
||||
=== 23. Nested Arrays ===
|
||||
m[0][0]: 1
|
||||
m[0][2]: 3
|
||||
m[1][0]: 4
|
||||
m[1][2]: 6
|
||||
=== 24. String Comparison ===
|
||||
str-eq: true
|
||||
str-neq: true
|
||||
str-diff: false
|
||||
empty-eq: true
|
||||
=== 25. Array Loop Mutation ===
|
||||
loop-fill: 1 2 3 4
|
||||
compound: 13
|
||||
=== 26. #using ===
|
||||
using-x: 1
|
||||
using-y: 2
|
||||
using-z: 3
|
||||
pkt-id: 10
|
||||
pkt-ver: 42
|
||||
pkt-pay: 99
|
||||
sprite-px: 10
|
||||
sprite-r: 255
|
||||
sprite-scale: 1
|
||||
say: hello (len=5)
|
||||
n=42
|
||||
=== Tuples ===
|
||||
40
|
||||
2
|
||||
10
|
||||
10
|
||||
42
|
||||
0
|
||||
0
|
||||
=== UFCS Aliases ===
|
||||
42
|
||||
42
|
||||
42
|
||||
42
|
||||
42
|
||||
3
|
||||
3
|
||||
3
|
||||
2
|
||||
1
|
||||
99
|
||||
=== Tuple Operators ===
|
||||
true
|
||||
false
|
||||
true
|
||||
false
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
1
|
||||
2
|
||||
1
|
||||
2
|
||||
1
|
||||
2
|
||||
true
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
false
|
||||
--- directory imports ---
|
||||
7
|
||||
30
|
||||
hello from testpkg
|
||||
cwd-import-ok
|
||||
--- pipe operator ---
|
||||
7
|
||||
30
|
||||
14
|
||||
hello world
|
||||
piped ok!
|
||||
alloc len: 5
|
||||
alloc[0]: 10
|
||||
alloc[4]: 50
|
||||
bytes len: 3
|
||||
--- allocators ---
|
||||
gpa allocs: 2
|
||||
gpa final: 0
|
||||
arena chunks: 1
|
||||
arena overflow: 2
|
||||
arena a1: 42
|
||||
arena a3: 99
|
||||
arena reset idx: 0
|
||||
arena reset gpa: 1
|
||||
arena deinit: 0
|
||||
buf pos: 48
|
||||
buf overflow: 0
|
||||
buf reset: 0
|
||||
1 == (1)
|
||||
(1) == 1
|
||||
1 == 1
|
||||
--- optionals ---
|
||||
opt x: 42
|
||||
opt y: null
|
||||
unwrap: 10
|
||||
coalesce a: 42
|
||||
coalesce b: 99
|
||||
if-bind x: 7
|
||||
if-bind y: none
|
||||
match some: 55
|
||||
match none: 0
|
||||
wrap pos: 5
|
||||
wrap neg: null
|
||||
opt field default: null
|
||||
opt field set: 42
|
||||
opt param a: 42
|
||||
opt param b: 0
|
||||
opt param 7: 7
|
||||
generic opt 1: 5
|
||||
generic opt 2: 7
|
||||
generic opt 3: null
|
||||
chain some: 10
|
||||
chain none: 0
|
||||
chain print: 20
|
||||
chain null: null
|
||||
deep chain 1: 99
|
||||
deep chain 2: 0
|
||||
narrow x: 42
|
||||
narrow y else: null
|
||||
narrow else x: 42
|
||||
guard some: 42
|
||||
guard none: 0
|
||||
and both: 10 20
|
||||
and one null
|
||||
or guard: 7
|
||||
or guard null: 0
|
||||
nested narrow: 10 20
|
||||
guard loop: 3
|
||||
block-lambda: 50
|
||||
block-lambda: 0
|
||||
block-lambda: 100
|
||||
hello block
|
||||
named-fn-type: 7
|
||||
xx-fnptr: 142
|
||||
closure-type: fn_ptr-nonnull=true
|
||||
closure-type: env-null=true
|
||||
closure-call: 15
|
||||
auto-promote: 20
|
||||
auto-promote-var: 10
|
||||
closure-capture: 52
|
||||
closure-snapshot: 15
|
||||
closure-nocap: 14
|
||||
closure-multi: 33
|
||||
closure-block: 60
|
||||
closure-block: 0
|
||||
closure-block: 100
|
||||
[LOG] hello
|
||||
closure-hof: 30
|
||||
closure-hof-bare: 20
|
||||
closure-f32: 10.000000
|
||||
closure-bool: hello
|
||||
closure-2p: 107
|
||||
closure-3p: 61
|
||||
closure-mix: Alice is 35
|
||||
closure-rbool: false true
|
||||
closure-reduce: 115
|
||||
closure-factory: 105 110
|
||||
closure-struct: 10 20
|
||||
closure-compose: 30
|
||||
closure-indep: 15 50
|
||||
opt-closure: none
|
||||
opt-closure: 15
|
||||
opt-closure-btn: 1 99
|
||||
opt-closure-btn: null
|
||||
closure-ptr: 3
|
||||
closure-enum: 2
|
||||
closure-rstr: [INFO] ok
|
||||
closure-rstruct: 11 22
|
||||
closure-linear: 37
|
||||
closure-clamp: 0 100 255
|
||||
closure-compose2: 12
|
||||
closure-chain: 22
|
||||
closure-map: 3 6 9 12 15
|
||||
closure-filter: 3 [3 4 5]
|
||||
closure-sort: 5 4 3 2 1
|
||||
closure-fe: item 0=10
|
||||
closure-fe: item 1=20
|
||||
closure-fe: item 2=30
|
||||
closure-find: 2
|
||||
closure-any: false true
|
||||
closure-struct-field: -5
|
||||
closure-btn: 1 99
|
||||
closure-counter: 1 2 3
|
||||
closure-acc: 105 115
|
||||
closure-loop: 115
|
||||
closure-reassign: 11
|
||||
closure-reassign: 20
|
||||
closure-snapstruct: 15
|
||||
closure-cap-promoted: 11
|
||||
closure-iife: 15
|
||||
closure-toggle: none
|
||||
closure-toggle: true
|
||||
closure-panel: main 800x600
|
||||
closure-chain-call: true
|
||||
closure-loop-0: 1
|
||||
closure-loop-1: 11
|
||||
closure-loop-4: 41
|
||||
closure-cond: 10
|
||||
closure-form: submitted
|
||||
closure-form: no cancel
|
||||
closure-null-env: true
|
||||
closure-slice: 10 20 30
|
||||
closure-arena: 15
|
||||
closure-gpa: 17 allocs=0
|
||||
closure-opt: 42
|
||||
closure-ropt: 50
|
||||
closure-ropt: none
|
||||
closure-mixed: 10
|
||||
closure-mixed: 15
|
||||
closure-mixed: 25
|
||||
closure-factory-indep: 20 30 40
|
||||
closure-deep-chain: 122
|
||||
closure-8cap: 36
|
||||
closure-4param: 10
|
||||
closure-shared-ptr: 7
|
||||
closure-f64: true
|
||||
closure-zerocap: 49 true
|
||||
closure-struct-method: 7
|
||||
closure-multi-factory: 10
|
||||
closure-multi-factory: 20
|
||||
closure-multi-factory: 30
|
||||
closure-bool-cap: true false
|
||||
closure-as-arg: 142
|
||||
closure-strfmt: hello world
|
||||
closure-ptr-before: 10
|
||||
closure-ptr-after: 42
|
||||
closure-neg: -70
|
||||
closure-proto-cap: true
|
||||
closure-chain-factory: 37
|
||||
closure-while-cond: 3
|
||||
closure-infer: 7
|
||||
closure-infer-arg: 15
|
||||
closure-infer-block: 12
|
||||
closure-infer-cap: 105
|
||||
closure-infer-factory: 35
|
||||
closure-infer-compose: 11
|
||||
closure-infer-void: 42
|
||||
=== Protocols ===
|
||||
P1.1: 3
|
||||
P1.2: 30
|
||||
P2.1: 42
|
||||
P2.2: 150
|
||||
P2.3: 5 10
|
||||
P3.1: 5
|
||||
P3.2: 12
|
||||
hi hi
|
||||
P4.1: 2
|
||||
yo yo
|
||||
P4.2: 2
|
||||
P4.3: 6 2
|
||||
P5.1: true false
|
||||
P5.2: 10 20
|
||||
P5.5: true false
|
||||
P5.3: true false
|
||||
P6.1: true false
|
||||
P6.2: true false
|
||||
P6.3: true false
|
||||
P6.4: 40
|
||||
P6.5: 20
|
||||
P7.1: 30
|
||||
P7.2: 10 300
|
||||
P2.6: 5 10
|
||||
=== DONE ===
|
||||
|
||||
Reference in New Issue
Block a user