mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
commit
6bbb168f66
@ -1485,6 +1485,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
const type_node = var_decl.getTrailer("type_node") orelse
|
||||
break :blk null;
|
||||
|
||||
// Temporary arena for the zir instructions.
|
||||
var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer type_scope_arena.deinit();
|
||||
var type_scope: Scope.GenZIR = .{
|
||||
@ -1539,7 +1540,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
try self.coerce(&inner_block.base, some, ret.operand)
|
||||
else
|
||||
ret.operand;
|
||||
const val = try self.resolveConstValue(&inner_block.base, coerced);
|
||||
const val = coerced.value() orelse
|
||||
return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
|
||||
|
||||
var_type = explicit_type orelse try ret.operand.ty.copy(block_scope.arena);
|
||||
break :blk try val.copy(block_scope.arena);
|
||||
@ -1603,7 +1605,41 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
}
|
||||
return type_changed;
|
||||
},
|
||||
.Comptime => @panic("TODO comptime decl"),
|
||||
.Comptime => {
|
||||
const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node);
|
||||
|
||||
decl.analysis = .in_progress;
|
||||
|
||||
// A comptime decl does not store any value so we can just deinit this arena after analysis is done.
|
||||
var analysis_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer analysis_arena.deinit();
|
||||
var gen_scope: Scope.GenZIR = .{
|
||||
.decl = decl,
|
||||
.arena = &analysis_arena.allocator,
|
||||
.parent = decl.scope,
|
||||
};
|
||||
defer gen_scope.instructions.deinit(self.gpa);
|
||||
|
||||
// TODO comptime scope here
|
||||
_ = try astgen.expr(self, &gen_scope.base, .none, comptime_decl.expr);
|
||||
|
||||
var block_scope: Scope.Block = .{
|
||||
.parent = null,
|
||||
.func = null,
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &analysis_arena.allocator,
|
||||
};
|
||||
defer block_scope.instructions.deinit(self.gpa);
|
||||
|
||||
_ = try zir_sema.analyzeBody(self, &block_scope.base, .{
|
||||
.instructions = gen_scope.instructions.items,
|
||||
});
|
||||
|
||||
decl.analysis = .complete;
|
||||
decl.generation = self.generation;
|
||||
return true;
|
||||
},
|
||||
.Use => @panic("TODO usingnamespace decl"),
|
||||
else => unreachable,
|
||||
}
|
||||
@ -1794,7 +1830,16 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
}
|
||||
}
|
||||
} else if (src_decl.castTag(.Comptime)) |comptime_node| {
|
||||
log.err("TODO: analyze comptime decl", .{});
|
||||
const name_index = self.getNextAnonNameIndex();
|
||||
const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index});
|
||||
defer self.gpa.free(name);
|
||||
|
||||
const name_hash = root_scope.fullyQualifiedNameHash(name);
|
||||
const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
|
||||
|
||||
const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
root_scope.decls.appendAssumeCapacity(new_decl);
|
||||
self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
|
||||
} else if (src_decl.castTag(.ContainerField)) |container_field| {
|
||||
log.err("TODO: analyze container field", .{});
|
||||
} else if (src_decl.castTag(.TestDecl)) |test_decl| {
|
||||
@ -2429,7 +2474,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
|
||||
if (decl_tv.val.tag() == .variable) {
|
||||
return self.analyzeVarRef(scope, src, decl_tv);
|
||||
}
|
||||
const ty = try self.singlePtrType(scope, src, false, decl_tv.ty);
|
||||
const ty = try self.simplePtrType(scope, src, decl_tv.ty, false, .One);
|
||||
const val_payload = try scope.arena().create(Value.Payload.DeclRef);
|
||||
val_payload.* = .{ .decl = decl };
|
||||
|
||||
@ -2442,7 +2487,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
|
||||
fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
|
||||
const variable = tv.val.cast(Value.Payload.Variable).?.variable;
|
||||
|
||||
const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty);
|
||||
const ty = try self.simplePtrType(scope, src, tv.ty, variable.is_mutable, .One);
|
||||
if (!variable.is_mutable and !variable.is_extern) {
|
||||
const val_payload = try scope.arena().create(Value.Payload.RefVal);
|
||||
val_payload.* = .{ .val = variable.init };
|
||||
@ -2766,7 +2811,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
|
||||
|
||||
// T to ?T
|
||||
if (dest_type.zigTypeTag() == .Optional) {
|
||||
var buf: Type.Payload.Pointer = undefined;
|
||||
var buf: Type.Payload.PointerSimple = undefined;
|
||||
const child_type = dest_type.optionalChild(&buf);
|
||||
if (child_type.eql(inst.ty)) {
|
||||
return self.wrapOptional(scope, dest_type, inst);
|
||||
@ -3145,11 +3190,56 @@ pub fn floatSub(self: *Module, scope: *Scope, float_type: Type, src: usize, lhs:
|
||||
return Value.initPayload(val_payload);
|
||||
}
|
||||
|
||||
pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, elem_ty: Type) Allocator.Error!Type {
|
||||
pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
|
||||
if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
|
||||
return Type.initTag(.const_slice_u8);
|
||||
}
|
||||
// TODO stage1 type inference bug
|
||||
const T = Type.Tag;
|
||||
|
||||
const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
|
||||
type_payload.* = .{
|
||||
.base = .{
|
||||
.tag = switch (size) {
|
||||
.One => if (mutable) T.single_mut_pointer else T.single_const_pointer,
|
||||
.Many => if (mutable) T.many_mut_pointer else T.many_const_pointer,
|
||||
.C => if (mutable) T.c_mut_pointer else T.c_const_pointer,
|
||||
.Slice => if (mutable) T.mut_slice else T.const_slice,
|
||||
},
|
||||
},
|
||||
.pointee_type = elem_ty,
|
||||
};
|
||||
return Type.initPayload(&type_payload.base);
|
||||
}
|
||||
|
||||
pub fn ptrType(
|
||||
self: *Module,
|
||||
scope: *Scope,
|
||||
src: usize,
|
||||
elem_ty: Type,
|
||||
sentinel: ?Value,
|
||||
@"align": u32,
|
||||
bit_offset: u16,
|
||||
host_size: u16,
|
||||
mutable: bool,
|
||||
@"allowzero": bool,
|
||||
@"volatile": bool,
|
||||
size: std.builtin.TypeInfo.Pointer.Size,
|
||||
) Allocator.Error!Type {
|
||||
assert(host_size == 0 or bit_offset < host_size * 8);
|
||||
|
||||
// TODO check if type can be represented by simplePtrType
|
||||
const type_payload = try scope.arena().create(Type.Payload.Pointer);
|
||||
type_payload.* = .{
|
||||
.base = .{ .tag = if (mutable) .single_mut_pointer else .single_const_pointer },
|
||||
.pointee_type = elem_ty,
|
||||
.sentinel = sentinel,
|
||||
.@"align" = @"align",
|
||||
.bit_offset = bit_offset,
|
||||
.host_size = host_size,
|
||||
.@"allowzero" = @"allowzero",
|
||||
.mutable = mutable,
|
||||
.@"volatile" = @"volatile",
|
||||
.size = size,
|
||||
};
|
||||
return Type.initPayload(&type_payload.base);
|
||||
}
|
||||
@ -3157,7 +3247,7 @@ pub fn singlePtrType(self: *Module, scope: *Scope, src: usize, mutable: bool, el
|
||||
pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
|
||||
return Type.initPayload(switch (child_type.tag()) {
|
||||
.single_const_pointer => blk: {
|
||||
const payload = try scope.arena().create(Type.Payload.Pointer);
|
||||
const payload = try scope.arena().create(Type.Payload.PointerSimple);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .optional_single_const_pointer },
|
||||
.pointee_type = child_type.elemType(),
|
||||
@ -3165,7 +3255,7 @@ pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Er
|
||||
break :blk &payload.base;
|
||||
},
|
||||
.single_mut_pointer => blk: {
|
||||
const payload = try scope.arena().create(Type.Payload.Pointer);
|
||||
const payload = try scope.arena().create(Type.Payload.PointerSimple);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .optional_single_mut_pointer },
|
||||
.pointee_type = child_type.elemType(),
|
||||
|
||||
@ -262,6 +262,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
||||
.EnumLiteral => return rlWrap(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
|
||||
.MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
|
||||
.CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
|
||||
.SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
|
||||
|
||||
.Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
|
||||
.Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),
|
||||
@ -275,7 +276,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
||||
.NegationWrap => return mod.failNode(scope, node, "TODO implement astgen.expr for .NegationWrap", .{}),
|
||||
.Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
|
||||
.Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
|
||||
.SliceType => return mod.failNode(scope, node, "TODO implement astgen.expr for .SliceType", .{}),
|
||||
.Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),
|
||||
.ArrayAccess => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayAccess", .{}),
|
||||
.ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
|
||||
@ -569,43 +569,67 @@ fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) Inn
|
||||
return addZIRUnOp(mod, scope, src, .optional_type, operand);
|
||||
}
|
||||
|
||||
fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.op_token].start;
|
||||
return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
|
||||
}
|
||||
|
||||
fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.op_token].start;
|
||||
return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
|
||||
.Asterisk, .AsteriskAsterisk => .One,
|
||||
// TODO stage1 type inference bug
|
||||
.LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
|
||||
.Identifier => .C,
|
||||
else => .Many,
|
||||
}),
|
||||
else => unreachable,
|
||||
});
|
||||
}
|
||||
|
||||
fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
|
||||
const meta_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
|
||||
const simple = node.ptr_info.allowzero_token == null and
|
||||
node.ptr_info.align_info == null and
|
||||
node.ptr_info.volatile_token == null and
|
||||
node.ptr_info.sentinel == null;
|
||||
const simple = ptr_info.allowzero_token == null and
|
||||
ptr_info.align_info == null and
|
||||
ptr_info.volatile_token == null and
|
||||
ptr_info.sentinel == null;
|
||||
|
||||
if (simple) {
|
||||
const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
|
||||
return addZIRUnOp(mod, scope, src, if (node.ptr_info.const_token == null)
|
||||
.single_mut_ptr_type
|
||||
else
|
||||
.single_const_ptr_type, child_type);
|
||||
const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs);
|
||||
const mutable = ptr_info.const_token == null;
|
||||
// TODO stage1 type inference bug
|
||||
const T = zir.Inst.Tag;
|
||||
return addZIRUnOp(mod, scope, src, switch (size) {
|
||||
.One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
|
||||
.Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
|
||||
.C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
|
||||
.Slice => if (mutable) T.mut_slice_type else T.mut_slice_type,
|
||||
}, child_type);
|
||||
}
|
||||
|
||||
var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, "kw_args").field_type = .{};
|
||||
kw_args.@"allowzero" = node.ptr_info.allowzero_token != null;
|
||||
if (node.ptr_info.align_info) |some| {
|
||||
kw_args.size = size;
|
||||
kw_args.@"allowzero" = ptr_info.allowzero_token != null;
|
||||
if (ptr_info.align_info) |some| {
|
||||
kw_args.@"align" = try expr(mod, scope, .none, some.node);
|
||||
if (some.bit_range) |bit_range| {
|
||||
kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
|
||||
kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
|
||||
}
|
||||
}
|
||||
kw_args.@"const" = node.ptr_info.const_token != null;
|
||||
kw_args.@"volatile" = node.ptr_info.volatile_token != null;
|
||||
if (node.ptr_info.sentinel) |some| {
|
||||
kw_args.mutable = ptr_info.const_token == null;
|
||||
kw_args.@"volatile" = ptr_info.volatile_token != null;
|
||||
if (ptr_info.sentinel) |some| {
|
||||
kw_args.sentinel = try expr(mod, scope, .none, some);
|
||||
}
|
||||
|
||||
const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
|
||||
const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs);
|
||||
if (kw_args.sentinel) |some| {
|
||||
kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
|
||||
}
|
||||
@ -1271,7 +1295,7 @@ fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStr
|
||||
i += 1;
|
||||
}
|
||||
const slice = tree.tokenSlice(line);
|
||||
mem.copy(u8, bytes[i..], slice[2..slice.len - 1]);
|
||||
mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
|
||||
i += slice.len - 3;
|
||||
}
|
||||
|
||||
|
||||
@ -2060,7 +2060,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
if (typed_value.val.isNull())
|
||||
return MCValue{ .immediate = 0 };
|
||||
|
||||
var buf: Type.Payload.Pointer = undefined;
|
||||
var buf: Type.Payload.PointerSimple = undefined;
|
||||
return self.genTypedValue(src, .{
|
||||
.ty = typed_value.ty.optionalChild(&buf),
|
||||
.val = typed_value.val,
|
||||
|
||||
@ -66,10 +66,18 @@ pub const Type = extern union {
|
||||
.function => return .Fn,
|
||||
|
||||
.array, .array_u8_sentinel_0, .array_u8, .array_sentinel => return .Array,
|
||||
.single_const_pointer => return .Pointer,
|
||||
.single_mut_pointer => return .Pointer,
|
||||
.single_const_pointer_to_comptime_int => return .Pointer,
|
||||
.const_slice_u8 => return .Pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.pointer,
|
||||
=> return .Pointer,
|
||||
|
||||
.optional,
|
||||
.optional_single_const_pointer,
|
||||
@ -108,13 +116,19 @@ pub const Type = extern union {
|
||||
return @fieldParentPtr(T, "base", self.ptr_otherwise);
|
||||
}
|
||||
|
||||
pub fn castPointer(self: Type) ?*Payload.Pointer {
|
||||
pub fn castPointer(self: Type) ?*Payload.PointerSimple {
|
||||
return switch (self.tag()) {
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
=> @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise),
|
||||
=> @fieldParentPtr(Payload.PointerSimple, "base", self.ptr_otherwise),
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
@ -198,8 +212,8 @@ pub const Type = extern union {
|
||||
return true;
|
||||
},
|
||||
.Optional => {
|
||||
var buf_a: Payload.Pointer = undefined;
|
||||
var buf_b: Payload.Pointer = undefined;
|
||||
var buf_a: Payload.PointerSimple = undefined;
|
||||
var buf_b: Payload.PointerSimple = undefined;
|
||||
return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
|
||||
},
|
||||
.Float,
|
||||
@ -263,7 +277,7 @@ pub const Type = extern union {
|
||||
}
|
||||
},
|
||||
.Optional => {
|
||||
var buf: Payload.Pointer = undefined;
|
||||
var buf: Payload.PointerSimple = undefined;
|
||||
std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
|
||||
},
|
||||
.Float,
|
||||
@ -374,9 +388,34 @@ pub const Type = extern union {
|
||||
.optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
=> return self.copyPayloadSingleField(allocator, Payload.Pointer, "pointee_type"),
|
||||
=> return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
|
||||
|
||||
.pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
|
||||
const new_payload = try allocator.create(Payload.Pointer);
|
||||
new_payload.* = .{
|
||||
.base = payload.base,
|
||||
|
||||
.pointee_type = try payload.pointee_type.copy(allocator),
|
||||
.sentinel = if (payload.sentinel) |some| try some.copy(allocator) else null,
|
||||
.@"align" = payload.@"align",
|
||||
.bit_offset = payload.bit_offset,
|
||||
.host_size = payload.host_size,
|
||||
.@"allowzero" = payload.@"allowzero",
|
||||
.mutable = payload.mutable,
|
||||
.@"volatile" = payload.@"volatile",
|
||||
.size = payload.size,
|
||||
};
|
||||
return Type{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,17 +521,53 @@ pub const Type = extern union {
|
||||
continue;
|
||||
},
|
||||
.single_const_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("*const ");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.single_mut_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("*");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.many_const_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("[*]const ");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.many_mut_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("[*]");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.c_const_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("[*c]const ");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.c_mut_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("[*c]");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.const_slice => {
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("[]const ");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.mut_slice => {
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("[]");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.int_signed => {
|
||||
const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
|
||||
return out_stream.print("i{}", .{payload.bits});
|
||||
@ -508,17 +583,45 @@ pub const Type = extern union {
|
||||
continue;
|
||||
},
|
||||
.optional_single_const_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("?*const ");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.optional_single_mut_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
|
||||
const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("?*");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
|
||||
.pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
|
||||
if (payload.sentinel) |some| switch (payload.size) {
|
||||
.One, .C => unreachable,
|
||||
.Many => try out_stream.writeAll("[*:{}]"),
|
||||
.Slice => try out_stream.writeAll("[:{}]"),
|
||||
} else switch (payload.size) {
|
||||
.One => try out_stream.writeAll("*"),
|
||||
.Many => try out_stream.writeAll("[*]"),
|
||||
.C => try out_stream.writeAll("[*c]"),
|
||||
.Slice => try out_stream.writeAll("[]"),
|
||||
}
|
||||
if (payload.@"align" != 0) {
|
||||
try out_stream.print("align({}", .{payload.@"align"});
|
||||
|
||||
if (payload.bit_offset != 0) {
|
||||
try out_stream.print(":{}:{}", .{ payload.bit_offset, payload.host_size });
|
||||
}
|
||||
try out_stream.writeAll(") ");
|
||||
}
|
||||
if (!payload.mutable) try out_stream.writeAll("const ");
|
||||
if (payload.@"volatile") try out_stream.writeAll("volatile ");
|
||||
if (payload.@"allowzero") try out_stream.writeAll("allowzero ");
|
||||
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
@ -616,9 +719,7 @@ pub const Type = extern union {
|
||||
// TODO lazy types
|
||||
.array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
|
||||
.array_u8 => self.arrayLen() != 0,
|
||||
.array_sentinel => self.elemType().hasCodeGenBits(),
|
||||
.single_const_pointer => self.elemType().hasCodeGenBits(),
|
||||
.single_mut_pointer => self.elemType().hasCodeGenBits(),
|
||||
.array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),
|
||||
.int_signed => self.cast(Payload.IntSigned).?.bits == 0,
|
||||
.int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,
|
||||
|
||||
@ -669,10 +770,23 @@ pub const Type = extern union {
|
||||
.const_slice_u8,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8),
|
||||
|
||||
.pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
|
||||
|
||||
if (payload.@"align" != 0) return payload.@"align";
|
||||
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
|
||||
},
|
||||
|
||||
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
|
||||
.c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
|
||||
.c_int => return @divExact(CType.int.sizeInBits(target), 8),
|
||||
@ -704,7 +818,7 @@ pub const Type = extern union {
|
||||
},
|
||||
|
||||
.optional => {
|
||||
var buf: Payload.Pointer = undefined;
|
||||
var buf: Payload.PointerSimple = undefined;
|
||||
const child_type = self.optionalChild(&buf);
|
||||
if (!child_type.hasCodeGenBits()) return 1;
|
||||
|
||||
@ -744,6 +858,7 @@ pub const Type = extern union {
|
||||
.@"null" => unreachable,
|
||||
.@"undefined" => unreachable,
|
||||
.enum_literal => unreachable,
|
||||
.single_const_pointer_to_comptime_int => unreachable,
|
||||
|
||||
.u8,
|
||||
.i8,
|
||||
@ -766,15 +881,31 @@ pub const Type = extern union {
|
||||
.i32, .u32 => return 4,
|
||||
.i64, .u64 => return 8,
|
||||
|
||||
.isize,
|
||||
.usize,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
|
||||
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.const_slice_u8,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2,
|
||||
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8),
|
||||
=> {
|
||||
if (self.elemType().hasCodeGenBits()) return 1;
|
||||
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
|
||||
},
|
||||
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.pointer,
|
||||
=> {
|
||||
if (self.elemType().hasCodeGenBits()) return 0;
|
||||
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
|
||||
},
|
||||
|
||||
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
|
||||
.c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
|
||||
@ -805,7 +936,7 @@ pub const Type = extern union {
|
||||
},
|
||||
|
||||
.optional => {
|
||||
var buf: Payload.Pointer = undefined;
|
||||
var buf: Payload.PointerSimple = undefined;
|
||||
const child_type = self.optionalChild(&buf);
|
||||
if (!child_type.hasCodeGenBits()) return 1;
|
||||
|
||||
@ -872,12 +1003,20 @@ pub const Type = extern union {
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
=> false,
|
||||
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
=> true,
|
||||
|
||||
.pointer => self.cast(Payload.Pointer).?.size == .One,
|
||||
};
|
||||
}
|
||||
|
||||
@ -922,6 +1061,10 @@ pub const Type = extern union {
|
||||
.array_u8_sentinel_0,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.fn_noreturn_no_args,
|
||||
.fn_void_no_args,
|
||||
@ -936,7 +1079,12 @@ pub const Type = extern union {
|
||||
.enum_literal,
|
||||
=> false,
|
||||
|
||||
.const_slice_u8 => true,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.const_slice_u8,
|
||||
=> true,
|
||||
|
||||
.pointer => self.cast(Payload.Pointer).?.size == .Slice,
|
||||
};
|
||||
}
|
||||
|
||||
@ -987,16 +1135,24 @@ pub const Type = extern union {
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
.single_mut_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_mut_pointer,
|
||||
.optional,
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
.mut_slice,
|
||||
=> false,
|
||||
|
||||
.single_const_pointer,
|
||||
.many_const_pointer,
|
||||
.c_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.const_slice,
|
||||
=> true,
|
||||
|
||||
.pointer => !self.cast(Payload.Pointer).?.mutable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1048,6 +1204,12 @@ pub const Type = extern union {
|
||||
.int_signed,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.optional,
|
||||
@ -1055,6 +1217,11 @@ pub const Type = extern union {
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
=> false,
|
||||
|
||||
.pointer => {
|
||||
const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
|
||||
return payload.@"volatile";
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1063,7 +1230,7 @@ pub const Type = extern union {
|
||||
switch (self.tag()) {
|
||||
.optional_single_const_pointer, .optional_single_mut_pointer => return true,
|
||||
.optional => {
|
||||
var buf: Payload.Pointer = undefined;
|
||||
var buf: Payload.PointerSimple = undefined;
|
||||
const child_type = self.optionalChild(&buf);
|
||||
// optionals of zero sized pointers behave like bools
|
||||
if (!child_type.hasCodeGenBits()) return false;
|
||||
@ -1101,7 +1268,7 @@ pub const Type = extern union {
|
||||
=> return false,
|
||||
|
||||
.Optional => {
|
||||
var buf: Payload.Pointer = undefined;
|
||||
var buf: Payload.PointerSimple = undefined;
|
||||
return ty.optionalChild(&buf).isValidVarType(is_extern);
|
||||
},
|
||||
.Pointer, .Array => ty = ty.elemType(),
|
||||
@ -1164,15 +1331,23 @@ pub const Type = extern union {
|
||||
|
||||
.array => self.cast(Payload.Array).?.elem_type,
|
||||
.array_sentinel => self.cast(Payload.ArraySentinel).?.elem_type,
|
||||
.single_const_pointer => self.castPointer().?.pointee_type,
|
||||
.single_mut_pointer => self.castPointer().?.pointee_type,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
=> self.castPointer().?.pointee_type,
|
||||
.array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
|
||||
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
|
||||
.pointer => self.cast(Payload.Pointer).?.pointee_type,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts that the type is an optional.
|
||||
pub fn optionalChild(self: Type, buf: *Payload.Pointer) Type {
|
||||
pub fn optionalChild(self: Type, buf: *Payload.PointerSimple) Type {
|
||||
return switch (self.tag()) {
|
||||
.optional => self.cast(Payload.Optional).?.child_type,
|
||||
.optional_single_mut_pointer => {
|
||||
@ -1199,7 +1374,7 @@ pub const Type = extern union {
|
||||
return switch (self.tag()) {
|
||||
.optional => self.cast(Payload.Optional).?.child_type,
|
||||
.optional_single_mut_pointer, .optional_single_const_pointer => {
|
||||
const payload = try allocator.create(Payload.Pointer);
|
||||
const payload = try allocator.create(Payload.PointerSimple);
|
||||
payload.* = .{
|
||||
.base = .{
|
||||
.tag = if (self.tag() == .optional_single_const_pointer)
|
||||
@ -1256,8 +1431,15 @@ pub const Type = extern union {
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
@ -1316,8 +1498,15 @@ pub const Type = extern union {
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
@ -1366,8 +1555,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
@ -1427,8 +1623,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_signed,
|
||||
@ -1488,8 +1691,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.optional,
|
||||
@ -1547,8 +1757,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
@ -1635,8 +1852,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.u8,
|
||||
@ -1699,8 +1923,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.u8,
|
||||
@ -1762,8 +1993,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.u8,
|
||||
@ -1825,8 +2063,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.u8,
|
||||
@ -1885,8 +2130,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.u8,
|
||||
@ -1945,8 +2197,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.u8,
|
||||
@ -2025,8 +2284,15 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8,
|
||||
.array_u8_sentinel_0,
|
||||
.pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.optional,
|
||||
@ -2077,6 +2343,8 @@ pub const Type = extern union {
|
||||
.array_sentinel,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.c_void,
|
||||
.optional,
|
||||
.optional_single_mut_pointer,
|
||||
@ -2109,11 +2377,21 @@ pub const Type = extern union {
|
||||
ty = ty.elemType();
|
||||
continue;
|
||||
},
|
||||
.single_const_pointer, .single_mut_pointer => {
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
=> {
|
||||
const ptr = ty.castPointer().?;
|
||||
ty = ptr.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.pointer => {
|
||||
ty = ty.cast(Payload.Pointer).?.pointee_type;
|
||||
continue;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -2167,11 +2445,21 @@ pub const Type = extern union {
|
||||
.array_u8_sentinel_0,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.many_const_pointer,
|
||||
.many_mut_pointer,
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.optional,
|
||||
.optional_single_mut_pointer,
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
=> return false,
|
||||
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
=> return true,
|
||||
|
||||
.pointer => self.cast(Payload.Pointer).?.size == .C,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2229,8 +2517,15 @@ pub const Type = extern union {
|
||||
array_u8_sentinel_0,
|
||||
array,
|
||||
array_sentinel,
|
||||
pointer,
|
||||
single_const_pointer,
|
||||
single_mut_pointer,
|
||||
many_const_pointer,
|
||||
many_mut_pointer,
|
||||
c_const_pointer,
|
||||
c_mut_pointer,
|
||||
const_slice,
|
||||
mut_slice,
|
||||
int_signed,
|
||||
int_unsigned,
|
||||
function,
|
||||
@ -2272,7 +2567,7 @@ pub const Type = extern union {
|
||||
elem_type: Type,
|
||||
};
|
||||
|
||||
pub const Pointer = struct {
|
||||
pub const PointerSimple = struct {
|
||||
base: Payload,
|
||||
|
||||
pointee_type: Type,
|
||||
@ -2303,6 +2598,21 @@ pub const Type = extern union {
|
||||
|
||||
child_type: Type,
|
||||
};
|
||||
|
||||
pub const Pointer = struct {
|
||||
base: Payload = .{ .tag = .pointer },
|
||||
|
||||
pointee_type: Type,
|
||||
sentinel: ?Value,
|
||||
/// If zero use pointee_type.AbiAlign()
|
||||
@"align": u32,
|
||||
bit_offset: u16,
|
||||
host_size: u16,
|
||||
@"allowzero": bool,
|
||||
mutable: bool,
|
||||
@"volatile": bool,
|
||||
size: std.builtin.TypeInfo.Pointer.Size,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -194,10 +194,22 @@ pub const Inst = struct {
|
||||
shl,
|
||||
/// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
|
||||
shr,
|
||||
/// Create a const pointer type based on the element type. `*const T`
|
||||
/// Create a const pointer type with element type T. `*const T`
|
||||
single_const_ptr_type,
|
||||
/// Create a mutable pointer type based on the element type. `*T`
|
||||
/// Create a mutable pointer type with element type T. `*T`
|
||||
single_mut_ptr_type,
|
||||
/// Create a const pointer type with element type T. `[*]const T`
|
||||
many_const_ptr_type,
|
||||
/// Create a mutable pointer type with element type T. `[*]T`
|
||||
many_mut_ptr_type,
|
||||
/// Create a const pointer type with element type T. `[*c]const T`
|
||||
c_const_ptr_type,
|
||||
/// Create a mutable pointer type with element type T. `[*c]T`
|
||||
c_mut_ptr_type,
|
||||
/// Create a mutable slice type with element type T. `[]T`
|
||||
mut_slice_type,
|
||||
/// Create a const slice type with element type T. `[]T`
|
||||
const_slice_type,
|
||||
/// Create a pointer type with attributes
|
||||
ptr_type,
|
||||
/// Write a value to a pointer. For loading, see `deref`.
|
||||
@ -262,6 +274,12 @@ pub const Inst = struct {
|
||||
.typeof,
|
||||
.single_const_ptr_type,
|
||||
.single_mut_ptr_type,
|
||||
.many_const_ptr_type,
|
||||
.many_mut_ptr_type,
|
||||
.c_const_ptr_type,
|
||||
.c_mut_ptr_type,
|
||||
.mut_slice_type,
|
||||
.const_slice_type,
|
||||
.optional_type,
|
||||
.unwrap_optional_safe,
|
||||
.unwrap_optional_unsafe,
|
||||
@ -400,6 +418,12 @@ pub const Inst = struct {
|
||||
.shr,
|
||||
.single_const_ptr_type,
|
||||
.single_mut_ptr_type,
|
||||
.many_const_ptr_type,
|
||||
.many_mut_ptr_type,
|
||||
.c_const_ptr_type,
|
||||
.c_mut_ptr_type,
|
||||
.mut_slice_type,
|
||||
.const_slice_type,
|
||||
.store,
|
||||
.str,
|
||||
.sub,
|
||||
@ -856,9 +880,10 @@ pub const Inst = struct {
|
||||
@"align": ?*Inst = null,
|
||||
align_bit_start: ?*Inst = null,
|
||||
align_bit_end: ?*Inst = null,
|
||||
@"const": bool = true,
|
||||
mutable: bool = true,
|
||||
@"volatile": bool = false,
|
||||
sentinel: ?*Inst = null,
|
||||
size: std.builtin.TypeInfo.Pointer.Size = .One,
|
||||
},
|
||||
};
|
||||
|
||||
@ -2443,7 +2468,7 @@ const EmitZIR = struct {
|
||||
}
|
||||
},
|
||||
.Optional => {
|
||||
var buf: Type.Payload.Pointer = undefined;
|
||||
var buf: Type.Payload.PointerSimple = undefined;
|
||||
const inst = try self.arena.allocator.create(Inst.UnOp);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
|
||||
@ -51,8 +51,14 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
||||
.ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
|
||||
.ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
|
||||
.ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
|
||||
.single_const_ptr_type => return analyzeInstSingleConstPtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?),
|
||||
.single_mut_ptr_type => return analyzeInstSingleMutPtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?),
|
||||
.single_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One),
|
||||
.single_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One),
|
||||
.many_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many),
|
||||
.many_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_mut_ptr_type).?, true, .Many),
|
||||
.c_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_const_ptr_type).?, false, .C),
|
||||
.c_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.c_mut_ptr_type).?, true, .C),
|
||||
.const_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.const_slice_type).?, false, .Slice),
|
||||
.mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice),
|
||||
.ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?),
|
||||
.store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
|
||||
.str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
|
||||
@ -267,6 +273,14 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
|
||||
return val.toType();
|
||||
}
|
||||
|
||||
fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 {
|
||||
const new_inst = try resolveInst(mod, scope, old_inst);
|
||||
const coerced = try mod.coerce(scope, dest_type, new_inst);
|
||||
const val = try mod.resolveConstValue(scope, coerced);
|
||||
|
||||
return val.toUnsignedInt();
|
||||
}
|
||||
|
||||
pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
|
||||
const new_inst = try resolveInst(mod, scope, old_inst);
|
||||
const val = try mod.resolveConstValue(scope, new_inst);
|
||||
@ -324,7 +338,7 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
|
||||
|
||||
fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||
const operand = try resolveInst(mod, scope, inst.positionals.operand);
|
||||
const ptr_type = try mod.singlePtrType(scope, inst.base.src, false, operand.ty);
|
||||
const ptr_type = try mod.simplePtrType(scope, inst.base.src, operand.ty, false, .One);
|
||||
|
||||
if (operand.value()) |val| {
|
||||
const ref_payload = try scope.arena().create(Value.Payload.RefVal);
|
||||
@ -369,7 +383,7 @@ fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErro
|
||||
if (!var_type.isValidVarType(false)) {
|
||||
return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
|
||||
}
|
||||
const ptr_type = try mod.singlePtrType(scope, inst.base.src, true, var_type);
|
||||
const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
|
||||
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
|
||||
return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
|
||||
}
|
||||
@ -723,7 +737,7 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp
|
||||
}
|
||||
|
||||
const child_type = try operand.ty.elemType().optionalChildAlloc(scope.arena());
|
||||
const child_pointer = try mod.singlePtrType(scope, unwrap.base.src, operand.ty.isConstPtr(), child_type);
|
||||
const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One);
|
||||
|
||||
if (operand.value()) |val| {
|
||||
if (val.isNull()) {
|
||||
@ -940,7 +954,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
|
||||
// required a larger index.
|
||||
const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
|
||||
|
||||
const type_payload = try scope.arena().create(Type.Payload.Pointer);
|
||||
const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
|
||||
type_payload.* = .{
|
||||
.base = .{ .tag = .single_const_pointer },
|
||||
.pointee_type = array_ptr.ty.elemType().elemType(),
|
||||
@ -1311,18 +1325,49 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr
|
||||
return decl;
|
||||
}
|
||||
|
||||
fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||
fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst {
|
||||
const elem_type = try resolveType(mod, scope, inst.positionals.operand);
|
||||
const ty = try mod.singlePtrType(scope, inst.base.src, false, elem_type);
|
||||
return mod.constType(scope, inst.base.src, ty);
|
||||
}
|
||||
|
||||
fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||
const elem_type = try resolveType(mod, scope, inst.positionals.operand);
|
||||
const ty = try mod.singlePtrType(scope, inst.base.src, true, elem_type);
|
||||
const ty = try mod.simplePtrType(scope, inst.base.src, elem_type, mutable, size);
|
||||
return mod.constType(scope, inst.base.src, ty);
|
||||
}
|
||||
|
||||
fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst {
|
||||
return mod.fail(scope, inst.base.src, "TODO implement ptr_type", .{});
|
||||
// TODO lazy values
|
||||
const @"align" = if (inst.kw_args.@"align") |some|
|
||||
@truncate(u32, try resolveInt(mod, scope, some, Type.initTag(.u32)))
|
||||
else
|
||||
0;
|
||||
const bit_offset = if (inst.kw_args.align_bit_start) |some|
|
||||
@truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
|
||||
else
|
||||
0;
|
||||
const host_size = if (inst.kw_args.align_bit_end) |some|
|
||||
@truncate(u16, try resolveInt(mod, scope, some, Type.initTag(.u16)))
|
||||
else
|
||||
0;
|
||||
|
||||
if (host_size != 0 and bit_offset >= host_size * 8)
|
||||
return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{});
|
||||
|
||||
const sentinel = if (inst.kw_args.sentinel) |some|
|
||||
(try resolveInstConst(mod, scope, some)).val
|
||||
else
|
||||
null;
|
||||
|
||||
const elem_type = try resolveType(mod, scope, inst.positionals.child_type);
|
||||
|
||||
const ty = try mod.ptrType(
|
||||
scope,
|
||||
inst.base.src,
|
||||
elem_type,
|
||||
sentinel,
|
||||
@"align",
|
||||
bit_offset,
|
||||
host_size,
|
||||
inst.kw_args.mutable,
|
||||
inst.kw_args.@"allowzero",
|
||||
inst.kw_args.@"volatile",
|
||||
inst.kw_args.size,
|
||||
);
|
||||
return mod.constType(scope, inst.base.src, ty);
|
||||
}
|
||||
|
||||
@ -1,710 +0,0 @@
|
||||
const std = @import("std");
|
||||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
// self-hosted does not yet support PE executable files / COFF object files
|
||||
// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
|
||||
const linux_x64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const linux_riscv64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .riscv64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const wasi = std.zig.CrossTarget{
|
||||
.cpu_arch = .wasm32,
|
||||
.os_tag = .wasi,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
{
|
||||
var case = ctx.exe("hello world with updates", linux_x64);
|
||||
|
||||
case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
|
||||
|
||||
case.addError(
|
||||
\\export fn _start() noreturn {
|
||||
\\}
|
||||
, &[_][]const u8{":2:1: error: expected noreturn, found void"});
|
||||
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{rdx}" (14)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
// Now change the message only
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
|
||||
\\ [arg3] "{rdx}" (104)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
|
||||
);
|
||||
// Now we print it twice.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
|
||||
\\ [arg3] "{rdx}" (104)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
|
||||
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("hello world", linux_riscv64);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (64),
|
||||
\\ [arg1] "{a0}" (1),
|
||||
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{a2}" ("Hello, World!\n".len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (94),
|
||||
\\ [arg1] "{a0}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at comptime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{rdx}" (10 + 4)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at runtime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ if (a + b != 7) unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("substracting numbers at runtime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ sub(7, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn sub(a: u32, b: u32) void {
|
||||
\\ if (a - b != 3) unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("assert function", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 7);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Tests copying a register. For the `c = a + b`, it has to
|
||||
// preserve both a and b, because they are both used later.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ assert(e == 14);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// More stress on the liveness detection.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ const f = d + e; // 24
|
||||
\\ const g = e + f; // 38
|
||||
\\ const h = f + g; // 62
|
||||
\\ const i = g + h; // 100
|
||||
\\ assert(i == 100);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Requires a second move. The register allocator should figure out to re-use rax.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ const f = d + e; // 24
|
||||
\\ const g = e + f; // 38
|
||||
\\ const h = f + g; // 62
|
||||
\\ const i = g + h; // 100
|
||||
\\ const j = i + d; // 110
|
||||
\\ assert(j == 110);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Now we test integer return values.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 7);
|
||||
\\ assert(add(20, 10) == 30);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ return a + b;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Local mutable variables.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 7);
|
||||
\\ assert(add(20, 10) == 30);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ var x: u32 = undefined;
|
||||
\\ x = 0;
|
||||
\\ x += a;
|
||||
\\ x += b;
|
||||
\\ return x;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Optionals
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ const a: u32 = 2;
|
||||
\\ const b: ?u32 = a;
|
||||
\\ const c = b.?;
|
||||
\\ if (c != 2) unreachable;
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// While loops
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ var i: u32 = 0;
|
||||
\\ while (i < 4) : (i += 1) print();
|
||||
\\ assert(i == 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
|
||||
\\ [arg3] "{rdx}" (6)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"hello\nhello\nhello\nhello\n",
|
||||
);
|
||||
|
||||
// Labeled blocks (no conditional branch)
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 20);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ const x: u32 = blk: {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ break :blk e;
|
||||
\\ };
|
||||
\\ const y = x + a; // 17
|
||||
\\ const z = y + a; // 20
|
||||
\\ return z;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// This catches a possible bug in the logic for re-using dying operands.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 116);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ const x: u32 = blk: {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ const f = d + e; // 24
|
||||
\\ const g = e + f; // 38
|
||||
\\ const h = f + g; // 62
|
||||
\\ const i = g + h; // 100
|
||||
\\ const j = i + d; // 110
|
||||
\\ break :blk j;
|
||||
\\ };
|
||||
\\ const y = x + a; // 113
|
||||
\\ const z = y + a; // 116
|
||||
\\ return z;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Character literals and multiline strings.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ const ignore =
|
||||
\\ \\ cool thx
|
||||
\\ \\
|
||||
\\ ;
|
||||
\\ add('ぁ', '\x03');
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 12356);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Global const.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(aa, bb);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\const aa = 'ぁ';
|
||||
\\const bb = '\x03';
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 12356);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("wasm function calls", wasi);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ foo();
|
||||
\\ bar();
|
||||
\\ return 42;
|
||||
\\}
|
||||
\\fn foo() void {
|
||||
\\ bar();
|
||||
\\ bar();
|
||||
\\}
|
||||
\\fn bar() void {}
|
||||
,
|
||||
"42\n",
|
||||
);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() i64 {
|
||||
\\ bar();
|
||||
\\ foo();
|
||||
\\ foo();
|
||||
\\ bar();
|
||||
\\ foo();
|
||||
\\ bar();
|
||||
\\ return 42;
|
||||
\\}
|
||||
\\fn foo() void {
|
||||
\\ bar();
|
||||
\\}
|
||||
\\fn bar() void {}
|
||||
,
|
||||
"42\n",
|
||||
);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() f32 {
|
||||
\\ bar();
|
||||
\\ foo();
|
||||
\\ return 42.0;
|
||||
\\}
|
||||
\\fn foo() void {
|
||||
\\ bar();
|
||||
\\ bar();
|
||||
\\ bar();
|
||||
\\}
|
||||
\\fn bar() void {}
|
||||
,
|
||||
// This is what you get when you take the bits of the IEE-754
|
||||
// representation of 42.0 and reinterpret them as an unsigned
|
||||
// integer. Guess that's a bug in wasmtime.
|
||||
"1109917696\n",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
const std = @import("std");
|
||||
|
||||
const ErrorMsg = @import("../../src-self-hosted/Module.zig").ErrorMsg;
|
||||
|
||||
const linux_x64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
ctx.compileErrorZIR("call undefined local", linux_x64,
|
||||
\\@noreturn = primitive(noreturn)
|
||||
\\
|
||||
\\@start_fnty = fntype([], @noreturn, cc=Naked)
|
||||
\\@start = fn(@start_fnty, {
|
||||
\\ %0 = call(%test, [])
|
||||
\\})
|
||||
// TODO: address inconsistency in this message and the one in the next test
|
||||
, &[_][]const u8{":5:13: error: unrecognized identifier: %test"});
|
||||
|
||||
ctx.compileErrorZIR("call with non-existent target", linux_x64,
|
||||
\\@noreturn = primitive(noreturn)
|
||||
\\
|
||||
\\@start_fnty = fntype([], @noreturn, cc=Naked)
|
||||
\\@start = fn(@start_fnty, {
|
||||
\\ %0 = call(@notafunc, [])
|
||||
\\})
|
||||
\\@0 = str("_start")
|
||||
\\@1 = export(@0, "start")
|
||||
, &[_][]const u8{":5:13: error: decl 'notafunc' not found"});
|
||||
|
||||
// TODO: this error should occur at the call site, not the fntype decl
|
||||
ctx.compileErrorZIR("call naked function", linux_x64,
|
||||
\\@noreturn = primitive(noreturn)
|
||||
\\
|
||||
\\@start_fnty = fntype([], @noreturn, cc=Naked)
|
||||
\\@s = fn(@start_fnty, {})
|
||||
\\@start = fn(@start_fnty, {
|
||||
\\ %0 = call(@s, [])
|
||||
\\})
|
||||
\\@0 = str("_start")
|
||||
\\@1 = export(@0, "start")
|
||||
, &[_][]const u8{":4:9: error: unable to call function with naked calling convention"});
|
||||
|
||||
ctx.incrementalFailureZIR("exported symbol collision", linux_x64,
|
||||
\\@noreturn = primitive(noreturn)
|
||||
\\
|
||||
\\@start_fnty = fntype([], @noreturn)
|
||||
\\@start = fn(@start_fnty, {})
|
||||
\\
|
||||
\\@0 = str("_start")
|
||||
\\@1 = export(@0, "start")
|
||||
\\@2 = export(@0, "start")
|
||||
, &[_][]const u8{":8:13: error: exported symbol collision: _start"},
|
||||
\\@noreturn = primitive(noreturn)
|
||||
\\
|
||||
\\@start_fnty = fntype([], @noreturn)
|
||||
\\@start = fn(@start_fnty, {})
|
||||
\\
|
||||
\\@0 = str("_start")
|
||||
\\@1 = export(@0, "start")
|
||||
);
|
||||
|
||||
ctx.compileError("function redefinition", linux_x64,
|
||||
\\fn entry() void {}
|
||||
\\fn entry() void {}
|
||||
, &[_][]const u8{":2:4: error: redefinition of 'entry'"});
|
||||
|
||||
//ctx.incrementalFailure("function redefinition", linux_x64,
|
||||
// \\fn entry() void {}
|
||||
// \\fn entry() void {}
|
||||
//, &[_][]const u8{":2:4: error: redefinition of 'entry'"},
|
||||
// \\fn entry() void {}
|
||||
//);
|
||||
|
||||
//// TODO: need to make sure this works with other variants of export.
|
||||
//ctx.incrementalFailure("exported symbol collision", linux_x64,
|
||||
// \\export fn entry() void {}
|
||||
// \\export fn entry() void {}
|
||||
//, &[_][]const u8{":2:11: error: redefinition of 'entry'"},
|
||||
// \\export fn entry() void {}
|
||||
//);
|
||||
|
||||
// ctx.incrementalFailure("missing function name", linux_x64,
|
||||
// \\fn() void {}
|
||||
// , &[_][]const u8{":1:3: error: missing function name"},
|
||||
// \\fn a() void {}
|
||||
// );
|
||||
|
||||
// TODO: re-enable these tests.
|
||||
// https://github.com/ziglang/zig/issues/1364
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ return;
|
||||
// \\}
|
||||
//, "1.zig", 2, 5, "return expression outside function definition");
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\export fn entry() void {
|
||||
// \\ defer return;
|
||||
// \\}
|
||||
//, "1.zig", 2, 11, "cannot return from defer expression");
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\export fn entry() c_int {
|
||||
// \\ return 36893488147419103232;
|
||||
// \\}
|
||||
//, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var a: *align(4) align(4) i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 22, "Extra align qualifier");
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var b: *const const i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 19, "Extra align qualifier");
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var c: *volatile volatile i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 22, "Extra align qualifier");
|
||||
|
||||
//ctx.testCompileError(
|
||||
// \\comptime {
|
||||
// \\ var d: *allowzero allowzero i32 = 0;
|
||||
// \\}
|
||||
//, "1.zig", 2, 23, "Extra align qualifier");
|
||||
}
|
||||
@ -1,8 +1,726 @@
|
||||
const std = @import("std");
|
||||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
|
||||
// self-hosted does not yet support PE executable files / COFF object files
|
||||
// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
|
||||
const linux_x64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const linux_riscv64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .riscv64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const wasi = std.zig.CrossTarget{
|
||||
.cpu_arch = .wasm32,
|
||||
.os_tag = .wasi,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
try @import("compile_errors.zig").addCases(ctx);
|
||||
try @import("compare_output.zig").addCases(ctx);
|
||||
try @import("zir.zig").addCases(ctx);
|
||||
try @import("cbe.zig").addCases(ctx);
|
||||
{
|
||||
var case = ctx.exe("hello world with updates", linux_x64);
|
||||
|
||||
case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
|
||||
|
||||
// Incorrect return type
|
||||
case.addError(
|
||||
\\export fn _start() noreturn {
|
||||
\\}
|
||||
, &[_][]const u8{":2:1: error: expected noreturn, found void"});
|
||||
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{rdx}" (14)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
// Now change the message only
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
|
||||
\\ [arg3] "{rdx}" (104)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
|
||||
);
|
||||
// Now we print it twice.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
|
||||
\\ [arg3] "{rdx}" (104)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
|
||||
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("hello world", linux_riscv64);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (64),
|
||||
\\ [arg1] "{a0}" (1),
|
||||
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{a2}" ("Hello, World!\n".len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (94),
|
||||
\\ [arg1] "{a0}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at comptime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{rdx}" (10 + 4)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at runtime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ if (a + b != 7) unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("substracting numbers at runtime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ sub(7, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn sub(a: u32, b: u32) void {
|
||||
\\ if (a - b != 3) unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("assert function", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 7);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Tests copying a register. For the `c = a + b`, it has to
|
||||
// preserve both a and b, because they are both used later.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ assert(e == 14);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// More stress on the liveness detection.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ const f = d + e; // 24
|
||||
\\ const g = e + f; // 38
|
||||
\\ const h = f + g; // 62
|
||||
\\ const i = g + h; // 100
|
||||
\\ assert(i == 100);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Requires a second move. The register allocator should figure out to re-use rax.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(3, 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ const f = d + e; // 24
|
||||
\\ const g = e + f; // 38
|
||||
\\ const h = f + g; // 62
|
||||
\\ const i = g + h; // 100
|
||||
\\ const j = i + d; // 110
|
||||
\\ assert(j == 110);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Now we test integer return values.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 7);
|
||||
\\ assert(add(20, 10) == 30);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ return a + b;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Local mutable variables.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 7);
|
||||
\\ assert(add(20, 10) == 30);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ var x: u32 = undefined;
|
||||
\\ x = 0;
|
||||
\\ x += a;
|
||||
\\ x += b;
|
||||
\\ return x;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Optionals
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ const a: u32 = 2;
|
||||
\\ const b: ?u32 = a;
|
||||
\\ const c = b.?;
|
||||
\\ if (c != 2) unreachable;
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// While loops
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ var i: u32 = 0;
|
||||
\\ while (i < 4) : (i += 1) print();
|
||||
\\ assert(i == 4);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
|
||||
\\ [arg3] "{rdx}" (6)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"hello\nhello\nhello\nhello\n",
|
||||
);
|
||||
|
||||
// Labeled blocks (no conditional branch)
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 20);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ const x: u32 = blk: {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ break :blk e;
|
||||
\\ };
|
||||
\\ const y = x + a; // 17
|
||||
\\ const z = y + a; // 20
|
||||
\\ return z;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// This catches a possible bug in the logic for re-using dying operands.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 116);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ const x: u32 = blk: {
|
||||
\\ const c = a + b; // 7
|
||||
\\ const d = a + c; // 10
|
||||
\\ const e = d + b; // 14
|
||||
\\ const f = d + e; // 24
|
||||
\\ const g = e + f; // 38
|
||||
\\ const h = f + g; // 62
|
||||
\\ const i = g + h; // 100
|
||||
\\ const j = i + d; // 110
|
||||
\\ break :blk j;
|
||||
\\ };
|
||||
\\ const y = x + a; // 113
|
||||
\\ const z = y + a; // 116
|
||||
\\ return z;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Character literals and multiline strings.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ const ignore =
|
||||
\\ \\ cool thx
|
||||
\\ \\
|
||||
\\ ;
|
||||
\\ add('ぁ', '\x03');
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 12356);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Global const.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(aa, bb);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\const aa = 'ぁ';
|
||||
\\const bb = '\x03';
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 12356);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("wasm function calls", wasi);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() u32 {
|
||||
\\ foo();
|
||||
\\ bar();
|
||||
\\ return 42;
|
||||
\\}
|
||||
\\fn foo() void {
|
||||
\\ bar();
|
||||
\\ bar();
|
||||
\\}
|
||||
\\fn bar() void {}
|
||||
,
|
||||
"42\n",
|
||||
);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() i64 {
|
||||
\\ bar();
|
||||
\\ foo();
|
||||
\\ foo();
|
||||
\\ bar();
|
||||
\\ foo();
|
||||
\\ bar();
|
||||
\\ return 42;
|
||||
\\}
|
||||
\\fn foo() void {
|
||||
\\ bar();
|
||||
\\}
|
||||
\\fn bar() void {}
|
||||
,
|
||||
"42\n",
|
||||
);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() f32 {
|
||||
\\ bar();
|
||||
\\ foo();
|
||||
\\ return 42.0;
|
||||
\\}
|
||||
\\fn foo() void {
|
||||
\\ bar();
|
||||
\\ bar();
|
||||
\\ bar();
|
||||
\\}
|
||||
\\fn bar() void {}
|
||||
,
|
||||
// This is what you get when you take the bits of the IEE-754
|
||||
// representation of 42.0 and reinterpret them as an unsigned
|
||||
// integer. Guess that's a bug in wasmtime.
|
||||
"1109917696\n",
|
||||
);
|
||||
}
|
||||
|
||||
ctx.compileError("function redefinition", linux_x64,
|
||||
\\fn entry() void {}
|
||||
\\fn entry() void {}
|
||||
, &[_][]const u8{":2:4: error: redefinition of 'entry'"});
|
||||
|
||||
ctx.compileError("extern variable has no type", linux_x64,
|
||||
\\comptime {
|
||||
\\ _ = foo;
|
||||
\\}
|
||||
\\extern var foo;
|
||||
, &[_][]const u8{":4:1: error: unable to infer variable type"});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user