ERR/E4.1b: #caller_location + Source_Location (+ namespaced default fix, comptime flush)
Finishes Phase E4. `process.exit` / `assert` now report the caller's location.
#caller_location + Source_Location:
- new `hash_caller_location` token (lexer) + a leaf `caller_location` AST node
(parser primary-expr; sema + lsp arms).
- `Source_Location :: struct { file; line; col; func }` in std.sx.
- expandCallDefaults rewrites a `#caller_location` param default to a marker
carrying the CALL site's span + source_file.
- lowerCallerLocation synthesizes the struct: file + line:col via
errors.SourceLoc.compute over the diagnostics file→source map, stamped with
the enclosing (caller) function name. inferExprType resolves it to
Source_Location. Explicitly forwarding a Source_Location through an inner
call preserves the outermost site.
namespaced default-param expansion (pre-existing crash): expandCallDefaults
bailed on field_access callees, so `mod.fn(args)` with an omitted defaulted
param passed too few args → LLVM "incorrect number of arguments". Now resolves
the namespace fd (by field / qualified name); method-on-value calls (where
`self` shifts the count) stay excluded. Prerequisite for process.exit/assert
(always called namespaced) taking `loc = #caller_location`.
comptime flush: interp.callForeign flushes the interpreter's buffered print
output before invoking any host symbol, so a comptime diagnostic emitted just
before a terminating `_exit` (process.exit at comptime) survives.
process.exit/assert take `loc: Source_Location = #caller_location`; assert
prints `ASSERTION FAILED at <file>:<line>: <msg>`. examples 250 (assert
file:line), 251 (caller-location + forwarding). The two ffi-objc *.ir
snapshots are regenerated — adding Source_Location to std.sx renumbers the
global string pool the type/field-name tables index (benign, identical IR).
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// `assert` (ERR step E4.1): a false condition prints `ASSERTION FAILED: <msg>`
|
// `assert` (ERR steps E4.1 / E4.1b): a false condition prints `ASSERTION
|
||||||
// and exits 1; a true condition is a no-op. Built on `process.exit`. (The
|
// FAILED at <file>:<line>: <msg>` (the caller's location, via the
|
||||||
// caller's `file:line` in the message rides on `#caller_location` — E4.1b.)
|
// `#caller_location` default param) and exits 1; a true condition is a no-op.
|
||||||
// Expected exit code: 1.
|
// Built on `process.exit`. Expected exit code: 1.
|
||||||
|
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
proc :: #import "modules/process.sx";
|
proc :: #import "modules/process.sx";
|
||||||
|
|||||||
22
examples/251-caller-location.sx
Normal file
22
examples/251-caller-location.sx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// `#caller_location` (ERR step E4.1b). As a parameter's default value it
|
||||||
|
// resolves to a `Source_Location` of the CALL site — file, line:col, and the
|
||||||
|
// enclosing function — rather than the callee's signature. Explicitly
|
||||||
|
// forwarding a `Source_Location` through an inner call preserves the outermost
|
||||||
|
// site (so a logging wrapper reports where IT was called). Expected exit: 0.
|
||||||
|
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
note :: (loc: Source_Location = #caller_location) {
|
||||||
|
print("note from {} (line {})\n", loc.func, loc.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forwards its own caller location through to `note`.
|
||||||
|
wrap :: (loc: Source_Location = #caller_location) {
|
||||||
|
note(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
note(); // call site → func main
|
||||||
|
wrap(); // forwarded → still reports this line in main
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -130,25 +130,22 @@ clib_exit :: (code: s32) -> noreturn #foreign libc "_exit";
|
|||||||
// Stop the process immediately with exit code `code`. Does NOT unwind:
|
// Stop the process immediately with exit code `code`. Does NOT unwind:
|
||||||
// no `defer` / `onfail` cleanup runs, no error-trace frames are pushed —
|
// no `defer` / `onfail` cleanup runs, no error-trace frames are pushed —
|
||||||
// it's the POSIX `_exit(2)` syscall. At comptime (`#run`) it terminates the
|
// it's the POSIX `_exit(2)` syscall. At comptime (`#run`) it terminates the
|
||||||
// COMPILER with the same code after printing a short diagnostic; in compiled
|
// COMPILER with the same code after printing a diagnostic naming the call site
|
||||||
// code the `is_comptime()` branch folds away to just the syscall.
|
// (`loc` defaults to `#caller_location`); in compiled code the `is_comptime()`
|
||||||
//
|
// branch folds away to just the syscall.
|
||||||
// (PLAN-ERR E4.1 also specifies a `loc: Source_Location = #caller_location`
|
exit :: (code: u8, loc: Source_Location = #caller_location) -> noreturn {
|
||||||
// parameter and an interpreter-frame dump in the comptime branch. Both ride
|
|
||||||
// on the `#caller_location` directive — deferred to E4.1b.)
|
|
||||||
exit :: (code: u8) -> noreturn {
|
|
||||||
if is_comptime() {
|
if is_comptime() {
|
||||||
print("\nprocess.exit({}) called at comptime\n", code);
|
print("\nprocess.exit({}) called from {} at {}:{}\n", code, loc.func, loc.file, loc.line);
|
||||||
}
|
}
|
||||||
clib_exit(xx code);
|
clib_exit(xx code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abort with a message when `cond` is false. Prints `ASSERTION FAILED: <msg>`
|
// Abort with a message when `cond` is false. Prints `ASSERTION FAILED at
|
||||||
// then exits 1; a true condition is a no-op. (E4.1b adds the caller's
|
// <file>:<line>: <msg>` (the caller's location, via `#caller_location`) then
|
||||||
// `file:line` via `#caller_location`.)
|
// exits 1; a true condition is a no-op.
|
||||||
assert :: (cond: bool, msg: string) {
|
assert :: (cond: bool, msg: string, loc: Source_Location = #caller_location) {
|
||||||
if !cond {
|
if !cond {
|
||||||
print("ASSERTION FAILED: {}\n", msg);
|
print("ASSERTION FAILED at {}:{}: {}\n", loc.file, loc.line, msg);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ is_flags :: ($T: Type) -> bool #builtin;
|
|||||||
field_value_int :: ($T: Type, idx: s64) -> s64 #builtin;
|
field_value_int :: ($T: Type, idx: s64) -> s64 #builtin;
|
||||||
field_index :: ($T: Type, val: T) -> s64 #builtin;
|
field_index :: ($T: Type, val: T) -> s64 #builtin;
|
||||||
error_tag_name :: (e: $T) -> string #builtin;
|
error_tag_name :: (e: $T) -> string #builtin;
|
||||||
|
|
||||||
|
// Call-site location, synthesized by the `#caller_location` directive when it
|
||||||
|
// is a parameter's default value (ERR E4.1b). `process.exit` / `assert` use it
|
||||||
|
// to report where they were invoked.
|
||||||
|
Source_Location :: struct {
|
||||||
|
file: string;
|
||||||
|
line: s32;
|
||||||
|
col: s32;
|
||||||
|
func: string;
|
||||||
|
}
|
||||||
string :: []u8 #builtin;
|
string :: []u8 #builtin;
|
||||||
|
|
||||||
#import "allocators.sx";
|
#import "allocators.sx";
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ pub const Node = struct {
|
|||||||
try_expr: TryExpr,
|
try_expr: TryExpr,
|
||||||
catch_expr: CatchExpr,
|
catch_expr: CatchExpr,
|
||||||
onfail_stmt: OnFailStmt,
|
onfail_stmt: OnFailStmt,
|
||||||
|
/// `#caller_location` — a marker that, as a parameter default, resolves
|
||||||
|
/// to a `Source_Location` of the call site (ERR E4.1b). The node's
|
||||||
|
/// `span`/`source_file` carry the location (rewritten to the call site
|
||||||
|
/// during default expansion). No payload.
|
||||||
|
caller_location: void,
|
||||||
pack_index_type_expr: PackIndexTypeExpr,
|
pack_index_type_expr: PackIndexTypeExpr,
|
||||||
comptime_pack_ref: ComptimePackRef,
|
comptime_pack_ref: ComptimePackRef,
|
||||||
force_unwrap: ForceUnwrap,
|
force_unwrap: ForceUnwrap,
|
||||||
|
|||||||
@@ -426,6 +426,15 @@ pub const Interpreter = struct {
|
|||||||
|
|
||||||
fn callForeign(self: *Interpreter, func: *const inst_mod.Function, args: []const Value) InterpError!Value {
|
fn callForeign(self: *Interpreter, func: *const inst_mod.Function, args: []const Value) InterpError!Value {
|
||||||
const name = self.module.types.getString(func.name);
|
const name = self.module.types.getString(func.name);
|
||||||
|
|
||||||
|
// A foreign call may not return (e.g. `process.exit` → `_exit`), which
|
||||||
|
// would discard the interpreter's buffered `print` output (otherwise
|
||||||
|
// flushed only after `#run` completes). Flush it first so comptime
|
||||||
|
// diagnostics emitted just before a terminating call survive.
|
||||||
|
if (self.output.items.len > 0) {
|
||||||
|
_ = std.c.write(1, self.output.items.ptr, self.output.items.len);
|
||||||
|
self.output.clearRetainingCapacity();
|
||||||
|
}
|
||||||
const symbol = (host_ffi.lookupSymbol(self.alloc, name) catch return bailDetail("comptime foreign call: dlsym error looking up symbol")) orelse {
|
const symbol = (host_ffi.lookupSymbol(self.alloc, name) catch return bailDetail("comptime foreign call: dlsym error looking up symbol")) orelse {
|
||||||
if (last_bail_detail == null) last_bail_detail = "comptime foreign call: symbol not found via dlsym (target-specific binding called at compile time?)";
|
if (last_bail_detail == null) last_bail_detail = "comptime foreign call: symbol not found via dlsym (target-specific binding called at compile time?)";
|
||||||
return error.CannotEvalComptime;
|
return error.CannotEvalComptime;
|
||||||
|
|||||||
@@ -2657,6 +2657,7 @@ pub const Lowering = struct {
|
|||||||
|
|
||||||
.try_expr => |te| self.lowerTry(te.operand, node.span),
|
.try_expr => |te| self.lowerTry(te.operand, node.span),
|
||||||
.catch_expr => |ce| self.lowerCatch(&ce, node.span),
|
.catch_expr => |ce| self.lowerCatch(&ce, node.span),
|
||||||
|
.caller_location => self.lowerCallerLocation(node),
|
||||||
else => self.emitError("unknown_expr", node.span),
|
else => self.emitError("unknown_expr", node.span),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -11096,16 +11097,40 @@ pub const Lowering = struct {
|
|||||||
/// node with the defaults filled in. Returns null when no expansion is
|
/// node with the defaults filled in. Returns null when no expansion is
|
||||||
/// needed (callee unknown, all args provided, or no defaults available).
|
/// needed (callee unknown, all args provided, or no defaults available).
|
||||||
fn expandCallDefaults(self: *Lowering, c: *const ast.Call) ?*ast.Call {
|
fn expandCallDefaults(self: *Lowering, c: *const ast.Call) ?*ast.Call {
|
||||||
if (c.callee.data != .identifier) return null;
|
const fd = blk: {
|
||||||
const id_name = c.callee.data.identifier.name;
|
switch (c.callee.data) {
|
||||||
const eff_name = blk: {
|
.identifier => |id| {
|
||||||
const scoped = if (self.scope) |scope| scope.lookupFn(id_name) orelse id_name else id_name;
|
const eff_name = blk2: {
|
||||||
if (self.ufcs_alias_map.get(id_name)) |target| {
|
const scoped = if (self.scope) |scope| scope.lookupFn(id.name) orelse id.name else id.name;
|
||||||
break :blk if (self.scope) |scope| scope.lookupFn(target) orelse target else target;
|
if (self.ufcs_alias_map.get(id.name)) |target| {
|
||||||
|
break :blk2 if (self.scope) |scope| scope.lookupFn(target) orelse target else target;
|
||||||
|
}
|
||||||
|
break :blk2 scoped;
|
||||||
|
};
|
||||||
|
break :blk self.fn_ast_map.get(eff_name) orelse return null;
|
||||||
|
},
|
||||||
|
// Namespace call `mod.fn(args)` — args map directly to params
|
||||||
|
// (no `self` prepend), so default expansion is the same shape as
|
||||||
|
// a bare call. A METHOD call `value.method(args)` prepends `self`
|
||||||
|
// (arg/param counts are offset), so it's excluded: only treat the
|
||||||
|
// receiver as a namespace when it isn't a value in scope.
|
||||||
|
.field_access => |fa| {
|
||||||
|
const obj_name: ?[]const u8 = switch (fa.object.data) {
|
||||||
|
.identifier => |id| id.name,
|
||||||
|
.type_expr => |te| te.name,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
const name = obj_name orelse return null;
|
||||||
|
if (self.scope) |scope| {
|
||||||
|
if (scope.lookup(name) != null) return null; // method call on a value
|
||||||
|
}
|
||||||
|
if (self.global_names.contains(name)) return null;
|
||||||
|
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ name, fa.field }) catch fa.field;
|
||||||
|
break :blk self.fn_ast_map.get(qualified) orelse self.fn_ast_map.get(fa.field) orelse return null;
|
||||||
|
},
|
||||||
|
else => return null,
|
||||||
}
|
}
|
||||||
break :blk scoped;
|
|
||||||
};
|
};
|
||||||
const fd = self.fn_ast_map.get(eff_name) orelse return null;
|
|
||||||
if (c.args.len >= fd.params.len) return null;
|
if (c.args.len >= fd.params.len) return null;
|
||||||
var end: usize = c.args.len;
|
var end: usize = c.args.len;
|
||||||
while (end < fd.params.len) : (end += 1) {
|
while (end < fd.params.len) : (end += 1) {
|
||||||
@@ -11117,7 +11142,17 @@ pub const Lowering = struct {
|
|||||||
for (c.args, 0..) |arg, i| new_args[i] = arg;
|
for (c.args, 0..) |arg, i| new_args[i] = arg;
|
||||||
var i: usize = c.args.len;
|
var i: usize = c.args.len;
|
||||||
while (i < end) : (i += 1) {
|
while (i < end) : (i += 1) {
|
||||||
new_args[i] = fd.params[i].default_expr.?;
|
const def = fd.params[i].default_expr.?;
|
||||||
|
// `#caller_location` resolves at the CALL site, not the callee's
|
||||||
|
// signature: emit a fresh marker carrying the call's span + file so
|
||||||
|
// lowering synthesizes the caller's `Source_Location` (ERR E4.1b).
|
||||||
|
if (def.data == .caller_location) {
|
||||||
|
const n = self.alloc.create(ast.Node) catch return null;
|
||||||
|
n.* = .{ .span = c.callee.span, .data = .{ .caller_location = {} }, .source_file = c.callee.source_file };
|
||||||
|
new_args[i] = n;
|
||||||
|
} else {
|
||||||
|
new_args[i] = def;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const new_call = self.alloc.create(ast.Call) catch return null;
|
const new_call = self.alloc.create(ast.Call) catch return null;
|
||||||
new_call.* = .{ .callee = c.callee, .args = new_args };
|
new_call.* = .{ .callee = c.callee, .args = new_args };
|
||||||
@@ -13863,6 +13898,7 @@ pub const Lowering = struct {
|
|||||||
if (op_ty == channel) break :blk .void;
|
if (op_ty == channel) break :blk .void;
|
||||||
break :blk self.failableSuccessType(op_ty);
|
break :blk self.failableSuccessType(op_ty);
|
||||||
},
|
},
|
||||||
|
.caller_location => self.module.types.findByName(self.module.types.internString("Source_Location")) orelse .unresolved,
|
||||||
.if_expr => |ie| {
|
.if_expr => |ie| {
|
||||||
// If-else types as its branches' unified type. A `noreturn`
|
// If-else types as its branches' unified type. A `noreturn`
|
||||||
// branch (one that diverges — `return` / `raise` / `break` /
|
// branch (one that diverges — `return` / `raise` / `break` /
|
||||||
@@ -15574,6 +15610,46 @@ pub const Lowering = struct {
|
|||||||
/// callees, propagation from a value-carrying caller, and `try` inside an
|
/// callees, propagation from a value-carrying caller, and `try` inside an
|
||||||
/// `or` chain need the error-channel tuple ABI / fallback routing — those
|
/// `or` chain need the error-channel tuple ABI / fallback routing — those
|
||||||
/// land in E1.4b/E2, so we bail loudly here.
|
/// land in E1.4b/E2, so we bail loudly here.
|
||||||
|
/// Synthesize a `Source_Location` value for a `#caller_location` marker
|
||||||
|
/// (ERR E4.1b). The node's `span`/`source_file` are the CALL site (rewritten
|
||||||
|
/// by `expandCallDefaults`); resolve them to file / line:col against the
|
||||||
|
/// source text and stamp the enclosing (caller) function name.
|
||||||
|
fn lowerCallerLocation(self: *Lowering, node: *const Node) Ref {
|
||||||
|
const sl_tid = self.module.types.findByName(self.module.types.internString("Source_Location")) orelse {
|
||||||
|
if (self.diagnostics) |d| d.addFmt(.err, node.span, "`#caller_location` needs `Source_Location` (from std.sx) in scope", .{});
|
||||||
|
return self.builder.constInt(0, .void);
|
||||||
|
};
|
||||||
|
const file = node.source_file orelse self.current_source_file orelse (self.main_file orelse "");
|
||||||
|
const src = self.sourceForFile(file);
|
||||||
|
const loc = errors.SourceLoc.compute(src, node.span.start);
|
||||||
|
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.constString(self.module.types.internString(func_name)),
|
||||||
|
};
|
||||||
|
return self.builder.emit(.{ .struct_init = .{ .fields = self.alloc.dupe(Ref, &fields) catch unreachable } }, sl_tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The source text for `file`, via the diagnostics' file→source map (which
|
||||||
|
/// includes the main file). Empty if unavailable — line:col then degrade to
|
||||||
|
/// 1:1 rather than crash.
|
||||||
|
fn sourceForFile(self: *Lowering, file: []const u8) []const u8 {
|
||||||
|
const diags = self.diagnostics orelse return "";
|
||||||
|
if (diags.import_sources) |is| {
|
||||||
|
if (is.get(file)) |s| return s;
|
||||||
|
}
|
||||||
|
return diags.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Name of the function currently being lowered (the caller, at a
|
||||||
|
/// `#caller_location` site), or "" outside any function.
|
||||||
|
fn currentFunctionName(self: *Lowering) []const u8 {
|
||||||
|
const fid = self.builder.func orelse return "";
|
||||||
|
return self.module.types.getString(self.module.functions.items[@intFromEnum(fid)].name);
|
||||||
|
}
|
||||||
|
|
||||||
fn lowerTry(self: *Lowering, operand: *const Node, span: ast.Span) Ref {
|
fn lowerTry(self: *Lowering, operand: *const Node, span: ast.Span) Ref {
|
||||||
// (1) `try` is legal only inside a failable function.
|
// (1) `try` is legal only inside a failable function.
|
||||||
const caller_ret = self.effectiveReturnType() orelse {
|
const caller_ret = self.effectiveReturnType() orelse {
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ pub const Lexer = struct {
|
|||||||
.{ "#jni_main", Tag.hash_jni_main },
|
.{ "#jni_main", Tag.hash_jni_main },
|
||||||
.{ "#selector", Tag.hash_selector },
|
.{ "#selector", Tag.hash_selector },
|
||||||
.{ "#property", Tag.hash_property },
|
.{ "#property", Tag.hash_property },
|
||||||
|
.{ "#caller_location", Tag.hash_caller_location },
|
||||||
};
|
};
|
||||||
inline for (directives) |d| {
|
inline for (directives) |d| {
|
||||||
const keyword = d[0];
|
const keyword = d[0];
|
||||||
|
|||||||
@@ -1709,6 +1709,7 @@ pub const Server = struct {
|
|||||||
.hash_jni_main,
|
.hash_jni_main,
|
||||||
.hash_selector,
|
.hash_selector,
|
||||||
.hash_property,
|
.hash_property,
|
||||||
|
.hash_caller_location,
|
||||||
=> ST.keyword,
|
=> ST.keyword,
|
||||||
|
|
||||||
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
.kw_f32, .kw_f64, .kw_Type, .kw_Self => ST.type_,
|
||||||
|
|||||||
@@ -2773,6 +2773,10 @@ pub const Parser = struct {
|
|||||||
const inner = try self.parseExpr();
|
const inner = try self.parseExpr();
|
||||||
return try self.createNode(start, .{ .comptime_expr = .{ .expr = inner } });
|
return try self.createNode(start, .{ .comptime_expr = .{ .expr = inner } });
|
||||||
},
|
},
|
||||||
|
.hash_caller_location => {
|
||||||
|
self.advance();
|
||||||
|
return try self.createNode(start, .{ .caller_location = {} });
|
||||||
|
},
|
||||||
.hash_objc_call, .hash_jni_call, .hash_jni_static_call => {
|
.hash_objc_call, .hash_jni_call, .hash_jni_static_call => {
|
||||||
return try self.parseFfiIntrinsicCall();
|
return try self.parseFfiIntrinsicCall();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1136,6 +1136,7 @@ pub const Analyzer = struct {
|
|||||||
.try_expr => |te| {
|
.try_expr => |te| {
|
||||||
try self.analyzeNode(te.operand);
|
try self.analyzeNode(te.operand);
|
||||||
},
|
},
|
||||||
|
.caller_location => {}, // leaf marker (ERR E4.1b) — no sub-nodes
|
||||||
.catch_expr => |ce| {
|
.catch_expr => |ce| {
|
||||||
try self.analyzeNode(ce.operand);
|
try self.analyzeNode(ce.operand);
|
||||||
try self.pushScope();
|
try self.pushScope();
|
||||||
@@ -1587,6 +1588,7 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
|||||||
if (findNodeAtOffset(se.operand, offset)) |found| return found;
|
if (findNodeAtOffset(se.operand, offset)) |found| return found;
|
||||||
},
|
},
|
||||||
.break_expr, .continue_expr => {},
|
.break_expr, .continue_expr => {},
|
||||||
|
.caller_location => {},
|
||||||
.assignment => |asgn| {
|
.assignment => |asgn| {
|
||||||
if (findNodeAtOffset(asgn.target, offset)) |found| return found;
|
if (findNodeAtOffset(asgn.target, offset)) |found| return found;
|
||||||
if (findNodeAtOffset(asgn.value, offset)) |found| return found;
|
if (findNodeAtOffset(asgn.value, offset)) |found| return found;
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ pub const Tag = enum {
|
|||||||
hash_jni_method_descriptor, // `#jni_method_descriptor("(Sig)Ret")` per-method JNI descriptor override
|
hash_jni_method_descriptor, // `#jni_method_descriptor("(Sig)Ret")` per-method JNI descriptor override
|
||||||
hash_selector, // `#selector("explicit:string")` per-method Obj-C selector override (Phase 3.2)
|
hash_selector, // `#selector("explicit:string")` per-method Obj-C selector override (Phase 3.2)
|
||||||
hash_property, // `#property[(modifier, ...)]` field directive — synthesizes getter/setter dispatch (M2.2)
|
hash_property, // `#property[(modifier, ...)]` field directive — synthesizes getter/setter dispatch (M2.2)
|
||||||
|
hash_caller_location, // `#caller_location` — as a param default, synthesizes the call site's Source_Location (ERR E4.1b)
|
||||||
hash_jni_env, // `#jni_env(env) { body }` block-form env-scoping intrinsic
|
hash_jni_env, // `#jni_env(env) { body }` block-form env-scoping intrinsic
|
||||||
hash_jni_main, // `#jni_main #jni_class(...) { ... }` — class is the launchable Android Activity
|
hash_jni_main, // `#jni_main #jni_class(...) { ... }` — class is the launchable Android Activity
|
||||||
triple_minus, // ---
|
triple_minus, // ---
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
first assert passed
|
first assert passed
|
||||||
ASSERTION FAILED: two plus two is not five
|
ASSERTION FAILED at /Users/agra/projects/sx/examples/250-assert.sx:12: two plus two is not five
|
||||||
|
|||||||
1
tests/expected/251-caller-location.exit
Normal file
1
tests/expected/251-caller-location.exit
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
2
tests/expected/251-caller-location.txt
Normal file
2
tests/expected/251-caller-location.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
note from main (line 19)
|
||||||
|
note from main (line 20)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -954,5 +954,3 @@ entry:
|
|||||||
store ptr %selN, ptr @OBJC_SELECTOR_REFERENCES_actualSelectorName, align 8
|
store ptr %selN, ptr @OBJC_SELECTOR_REFERENCES_actualSelectorName, align 8
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user