Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2193,10 +2193,17 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
if (std.mem.eql(u8, call_name, "@src")) {
return analyser.instanceStdBuiltinType("SourceLocation");
}

if (std.mem.eql(u8, call_name, "@compileError")) {
return .{ .data = .{ .compile_error = node_handle }, .is_type_val = false };
}

if (std.mem.eql(u8, call_name, "@panic") or
std.mem.eql(u8, call_name, "@trap"))
{
return Type.fromIP(analyser, .noreturn_type, null);
}

if (std.mem.eql(u8, call_name, "@Vector")) {
if (params.len != 2) return null;

Expand Down Expand Up @@ -2414,16 +2421,21 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
.block_two,
.block_two_semicolon,
=> {
const has_zero_statements = switch (tree.nodeTag(node)) {
.block_two, .block_two_semicolon => tree.nodeData(node).opt_node_and_opt_node[0] == .none,
.block, .block_semicolon => false,
else => unreachable,
};
if (has_zero_statements) {
var buffer: [2]Ast.Node.Index = undefined;
const statements = tree.blockStatements(&buffer, node).?;
if (statements.len == 0) {
return Type.fromIP(analyser, .void_type, .void_value);
}

const label_token = ast.blockLabel(tree, node) orelse return null;
const label_token = ast.blockLabel(tree, node) orelse {
const last_statement = statements[statements.len - 1];
if (try analyser.resolveTypeOfNodeInternal(.of(last_statement, handle))) |ty| {
if (ty.typeOf(analyser).isNoreturnType()) {
return Type.fromIP(analyser, .noreturn_type, null);
}
}
return Type.fromIP(analyser, .void_type, .void_value);
};
const block_label = offsets.identifierTokenToNameSlice(tree, label_token);

// TODO: peer type resolution based on all `break` statements
Expand Down Expand Up @@ -3381,6 +3393,15 @@ pub const Type = struct {
};
}

pub fn isNoreturnType(self: Type) bool {
if (!self.is_type_val) return false;
return switch (self.data) {
.compile_error => true,
.ip_index => |payload| payload.index == .noreturn_type,
else => false,
};
}

pub fn typeDefinitionToken(self: Type) !?TokenWithHandle {
return switch (self.data) {
.container => |scope_handle| .{
Expand Down
45 changes: 45 additions & 0 deletions tests/analysis/block.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const empty_block = {};
// ^^^^^^^^^^^ (void)()

// zig fmt: off
const void_block = { _ = 1; };
// ^^^^^^^^^^ (void)()

const compile_error_block = { @compileError("foo"); };
// ^^^^^^^^^^^^^^^^^^^ (noreturn)()

const panic_block = { @panic("foo"); };
// ^^^^^^^^^^^ (noreturn)()

const labeled_block_0 = blk: { break :blk @as(i32, 1); };
// ^^^^^^^^^^^^^^^ (i32)()

// TODO this should be `i64`
const labeled_block_1 = blk: {
// ^^^^^^^^^^^^^^^ (i32)()
if (false) break :blk @as(i32, 1);
break :blk @as(i64, 2);
};
// zig fmt: on

pub fn main() void {
const return_block = {
return;
};
_ = return_block;
// ^^^^^^^^^^^^ (noreturn)()

for (0..1) |_| {
const break_block = {
break;
};
_ = break_block;
// ^^^^^^^^^^^ (noreturn)()

const continue_block = {
continue;
};
_ = continue_block;
// ^^^^^^^^^^^^^^ (noreturn)()
}
}
5 changes: 5 additions & 0 deletions tests/analysis/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ const abs_vector_i8 = @abs(@as(@Vector(4, i8), undefined));
const abs_vector_u8 = @abs(@as(@Vector(4, u8), undefined));
// ^^^^^^^^^^^^^ (@Vector(4,u8))()

const panic = @panic("foo");
// ^^^^^ (noreturn)()
const trap = @trap();
// ^^^^ (noreturn)()

comptime {
// Use @compileLog to verify the expected type with the compiler
// @compileLog(vector_builtin_13);
Expand Down