mirror of
https://github.com/ziglang/zig.git
synced 2026-01-02 03:25:01 +00:00
Merge pull request #7741 from FireFox317/optionals-llvm
stage2: add support for optionals in the LLVM backend
This commit is contained in:
commit
4b57fb5f23
@ -453,13 +453,23 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
|
||||
return rvalue(mod, scope, rl, result);
|
||||
},
|
||||
.unwrap_optional => {
|
||||
const operand = try expr(mod, scope, rl, node_datas[node].lhs);
|
||||
const op: zir.Inst.Tag = switch (rl) {
|
||||
.ref => .optional_payload_safe_ptr,
|
||||
else => .optional_payload_safe,
|
||||
};
|
||||
const src = token_starts[main_tokens[node]];
|
||||
return addZIRUnOp(mod, scope, src, op, operand);
|
||||
switch (rl) {
|
||||
.ref => return addZIRUnOp(
|
||||
mod,
|
||||
scope,
|
||||
src,
|
||||
.optional_payload_safe_ptr,
|
||||
try expr(mod, scope, .ref, node_datas[node].lhs),
|
||||
),
|
||||
else => return rvalue(mod, scope, rl, try addZIRUnOp(
|
||||
mod,
|
||||
scope,
|
||||
src,
|
||||
.optional_payload_safe,
|
||||
try expr(mod, scope, .none, node_datas[node].lhs),
|
||||
)),
|
||||
}
|
||||
},
|
||||
.block_two, .block_two_semicolon => {
|
||||
const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
|
||||
@ -1699,9 +1709,13 @@ fn orelseCatchExpr(
|
||||
setBlockResultLoc(&block_scope, rl);
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
// This could be a pointer or value depending on the `rl` parameter.
|
||||
// This could be a pointer or value depending on the `operand_rl` parameter.
|
||||
// We cannot use `block_scope.break_result_loc` because that has the bare
|
||||
// type, whereas this expression has the optional type. Later we make
|
||||
// up for this fact by calling rvalue on the else branch.
|
||||
block_scope.break_count += 1;
|
||||
const operand = try expr(mod, &block_scope.base, block_scope.break_result_loc, lhs);
|
||||
const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
|
||||
const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
|
||||
const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
|
||||
|
||||
const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
|
||||
@ -1753,6 +1767,10 @@ fn orelseCatchExpr(
|
||||
|
||||
// This could be a pointer or value depending on `unwrap_op`.
|
||||
const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
|
||||
const else_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload),
|
||||
};
|
||||
|
||||
return finishThenElseBlock(
|
||||
mod,
|
||||
@ -1766,7 +1784,7 @@ fn orelseCatchExpr(
|
||||
src,
|
||||
src,
|
||||
then_result,
|
||||
unwrapped_payload,
|
||||
else_result,
|
||||
block,
|
||||
block,
|
||||
);
|
||||
@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
|
||||
}
|
||||
}
|
||||
|
||||
/// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise:
|
||||
/// Returns ResultLoc.ty, where the type is determined by the input
|
||||
/// ResultLoc type, wrapped in an optional type. If the input ResultLoc
|
||||
/// has no type, .none is returned.
|
||||
fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc {
|
||||
switch (rl) {
|
||||
.ref => return ResultLoc.ref,
|
||||
.discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none,
|
||||
.ty => |elem_ty| {
|
||||
const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty);
|
||||
return ResultLoc{ .ty = wrapped_ty };
|
||||
},
|
||||
.ptr => |ptr_ty| {
|
||||
const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty);
|
||||
return ResultLoc{ .ty = wrapped_ty };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
|
||||
// Depending on whether the result location is a pointer or value, different
|
||||
// ZIR needs to be generated. In the former case we rely on storing to the
|
||||
|
||||
@ -397,6 +397,7 @@ pub const LLVMIRModule = struct {
|
||||
.block => try self.genBlock(inst.castTag(.block).?),
|
||||
.br => try self.genBr(inst.castTag(.br).?),
|
||||
.breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
|
||||
.br_void => try self.genBrVoid(inst.castTag(.br_void).?),
|
||||
.call => try self.genCall(inst.castTag(.call).?),
|
||||
.cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),
|
||||
.cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),
|
||||
@ -406,6 +407,10 @@ pub const LLVMIRModule = struct {
|
||||
.cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),
|
||||
.condbr => try self.genCondBr(inst.castTag(.condbr).?),
|
||||
.intcast => try self.genIntCast(inst.castTag(.intcast).?),
|
||||
.is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false),
|
||||
.is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true),
|
||||
.is_null => try self.genIsNull(inst.castTag(.is_null).?, false),
|
||||
.is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true),
|
||||
.load => try self.genLoad(inst.castTag(.load).?),
|
||||
.loop => try self.genLoop(inst.castTag(.loop).?),
|
||||
.not => try self.genNot(inst.castTag(.not).?),
|
||||
@ -414,6 +419,8 @@ pub const LLVMIRModule = struct {
|
||||
.store => try self.genStore(inst.castTag(.store).?),
|
||||
.sub => try self.genSub(inst.castTag(.sub).?),
|
||||
.unreach => self.genUnreach(inst.castTag(.unreach).?),
|
||||
.optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false),
|
||||
.optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true),
|
||||
.dbg_stmt => blk: {
|
||||
// TODO: implement debug info
|
||||
break :blk null;
|
||||
@ -534,21 +541,29 @@ pub const LLVMIRModule = struct {
|
||||
}
|
||||
|
||||
fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {
|
||||
// Get the block that we want to break to.
|
||||
var block = self.blocks.get(inst.block).?;
|
||||
_ = self.builder.buildBr(block.parent_bb);
|
||||
|
||||
// If the break doesn't break a value, then we don't have to add
|
||||
// the values to the lists.
|
||||
if (!inst.operand.ty.hasCodeGenBits()) return null;
|
||||
if (!inst.operand.ty.hasCodeGenBits()) {
|
||||
// TODO: in astgen these instructions should turn into `br_void` instructions.
|
||||
_ = self.builder.buildBr(block.parent_bb);
|
||||
} else {
|
||||
const val = try self.resolveInst(inst.operand);
|
||||
|
||||
// For the phi node, we need the basic blocks and the values of the
|
||||
// break instructions.
|
||||
try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
|
||||
// For the phi node, we need the basic blocks and the values of the
|
||||
// break instructions.
|
||||
try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
|
||||
try block.break_vals.append(self.gpa, val);
|
||||
|
||||
const val = try self.resolveInst(inst.operand);
|
||||
try block.break_vals.append(self.gpa, val);
|
||||
_ = self.builder.buildBr(block.parent_bb);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value {
|
||||
var block = self.blocks.get(inst.block).?;
|
||||
_ = self.builder.buildBr(block.parent_bb);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -591,6 +606,44 @@ pub const LLVMIRModule = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
|
||||
const operand = try self.resolveInst(inst.operand);
|
||||
|
||||
if (operand_is_ptr) {
|
||||
const index_type = self.context.intType(32);
|
||||
|
||||
var indices: [2]*const llvm.Value = .{
|
||||
index_type.constNull(),
|
||||
index_type.constInt(1, false),
|
||||
};
|
||||
|
||||
return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
|
||||
} else {
|
||||
return self.builder.buildExtractValue(operand, 1, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
|
||||
return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");
|
||||
}
|
||||
|
||||
fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
|
||||
const operand = try self.resolveInst(inst.operand);
|
||||
|
||||
if (operand_is_ptr) {
|
||||
const index_type = self.context.intType(32);
|
||||
|
||||
var indices: [2]*const llvm.Value = .{
|
||||
index_type.constNull(),
|
||||
index_type.constNull(),
|
||||
};
|
||||
|
||||
return self.builder.buildInBoundsGEP(operand, &indices, 2, "");
|
||||
} else {
|
||||
return self.builder.buildExtractValue(operand, 0, "");
|
||||
}
|
||||
}
|
||||
|
||||
fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
|
||||
const lhs = try self.resolveInst(inst.lhs);
|
||||
const rhs = try self.resolveInst(inst.rhs);
|
||||
@ -751,6 +804,13 @@ pub const LLVMIRModule = struct {
|
||||
// TODO: consider using buildInBoundsGEP2 for opaque pointers
|
||||
return self.builder.buildInBoundsGEP(val, &indices, 2, "");
|
||||
},
|
||||
.ref_val => {
|
||||
const elem_value = tv.val.castTag(.ref_val).?.data;
|
||||
const elem_type = tv.ty.castPointer().?.data;
|
||||
const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src));
|
||||
_ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca);
|
||||
return alloca;
|
||||
},
|
||||
else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
|
||||
},
|
||||
.Array => {
|
||||
@ -765,6 +825,29 @@ pub const LLVMIRModule = struct {
|
||||
return self.fail(src, "TODO handle more array values", .{});
|
||||
}
|
||||
},
|
||||
.Optional => {
|
||||
if (!tv.ty.isPtrLikeOptional()) {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const child_type = tv.ty.optionalChild(&buf);
|
||||
const llvm_child_type = try self.getLLVMType(child_type, src);
|
||||
|
||||
if (tv.val.tag() == .null_value) {
|
||||
var optional_values: [2]*const llvm.Value = .{
|
||||
llvm_child_type.constNull(),
|
||||
self.context.intType(1).constNull(),
|
||||
};
|
||||
return self.context.constStruct(&optional_values, 2, false);
|
||||
} else {
|
||||
var optional_values: [2]*const llvm.Value = .{
|
||||
try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
|
||||
self.context.intType(1).constAllOnes(),
|
||||
};
|
||||
return self.context.constStruct(&optional_values, 2, false);
|
||||
}
|
||||
} else {
|
||||
return self.fail(src, "TODO implement const of optional pointer", .{});
|
||||
}
|
||||
},
|
||||
else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
|
||||
}
|
||||
}
|
||||
@ -790,6 +873,20 @@ pub const LLVMIRModule = struct {
|
||||
const elem_type = try self.getLLVMType(t.elemType(), src);
|
||||
return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
|
||||
},
|
||||
.Optional => {
|
||||
if (!t.isPtrLikeOptional()) {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const child_type = t.optionalChild(&buf);
|
||||
|
||||
var optional_types: [2]*const llvm.Type = .{
|
||||
try self.getLLVMType(child_type, src),
|
||||
self.context.intType(1),
|
||||
};
|
||||
return self.context.structType(&optional_types, 2, false);
|
||||
} else {
|
||||
return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
|
||||
}
|
||||
},
|
||||
else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,9 +21,15 @@ pub const Context = opaque {
|
||||
pub const voidType = LLVMVoidTypeInContext;
|
||||
extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
|
||||
|
||||
pub const structType = LLVMStructTypeInContext;
|
||||
extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type;
|
||||
|
||||
pub const constString = LLVMConstStringInContext;
|
||||
extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;
|
||||
|
||||
pub const constStruct = LLVMConstStructInContext;
|
||||
extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value;
|
||||
|
||||
pub const createBasicBlock = LLVMCreateBasicBlockInContext;
|
||||
extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
|
||||
|
||||
@ -204,6 +210,9 @@ pub const Builder = opaque {
|
||||
|
||||
pub const buildPhi = LLVMBuildPhi;
|
||||
extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const buildExtractValue = LLVMBuildExtractValue;
|
||||
extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value;
|
||||
};
|
||||
|
||||
pub const IntPredicate = extern enum {
|
||||
|
||||
@ -550,11 +550,11 @@ pub const File = struct {
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| b: {
|
||||
log.debug("archive new_digest={x} readFile error: {s}", .{ digest, @errorName(err) });
|
||||
log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
break :b prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("archive digest={x} match - skipping invocation", .{digest});
|
||||
log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| blk: {
|
||||
log.debug("COFF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
|
||||
log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
// Handle this as a cache miss.
|
||||
break :blk prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("COFF LLD digest={x} match - skipping invocation", .{digest});
|
||||
log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
// Hot diggity dog! The output binary is already there.
|
||||
self.base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
log.debug("COFF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
|
||||
log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
|
||||
@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| blk: {
|
||||
log.debug("ELF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
|
||||
log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
// Handle this as a cache miss.
|
||||
break :blk prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("ELF LLD digest={x} match - skipping invocation", .{digest});
|
||||
log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
// Hot diggity dog! The output binary is already there.
|
||||
self.base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
log.debug("ELF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
|
||||
log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
|
||||
@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| blk: {
|
||||
log.debug("MachO LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
|
||||
log.debug("MachO LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
// Handle this as a cache miss.
|
||||
break :blk prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("MachO LLD digest={x} match - skipping invocation", .{digest});
|
||||
log.debug("MachO LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
// Hot diggity dog! The output binary is already there.
|
||||
self.base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
log.debug("MachO LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
|
||||
log.debug("MachO LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
|
||||
@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
|
||||
id_symlink_basename,
|
||||
&prev_digest_buf,
|
||||
) catch |err| blk: {
|
||||
log.debug("WASM LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
|
||||
log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
|
||||
// Handle this as a cache miss.
|
||||
break :blk prev_digest_buf[0..0];
|
||||
};
|
||||
if (mem.eql(u8, prev_digest, &digest)) {
|
||||
log.debug("WASM LLD digest={x} match - skipping invocation", .{digest});
|
||||
log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
|
||||
// Hot diggity dog! The output binary is already there.
|
||||
self.base.lock = man.toOwnedLock();
|
||||
return;
|
||||
}
|
||||
log.debug("WASM LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
|
||||
log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
|
||||
|
||||
// We are about to change the output file to be different, so we invalidate the build hash now.
|
||||
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
|
||||
|
||||
@ -299,6 +299,9 @@ pub const Inst = struct {
|
||||
xor,
|
||||
/// Create an optional type '?T'
|
||||
optional_type,
|
||||
/// Create an optional type '?T'. The operand is a pointer value. The optional type will
|
||||
/// be the type of the pointer element, wrapped in an optional.
|
||||
optional_type_from_ptr_elem,
|
||||
/// Create a union type.
|
||||
union_type,
|
||||
/// ?T => T with safety.
|
||||
@ -397,6 +400,7 @@ pub const Inst = struct {
|
||||
.mut_slice_type,
|
||||
.const_slice_type,
|
||||
.optional_type,
|
||||
.optional_type_from_ptr_elem,
|
||||
.optional_payload_safe,
|
||||
.optional_payload_unsafe,
|
||||
.optional_payload_safe_ptr,
|
||||
@ -597,6 +601,7 @@ pub const Inst = struct {
|
||||
.typeof,
|
||||
.xor,
|
||||
.optional_type,
|
||||
.optional_type_from_ptr_elem,
|
||||
.optional_payload_safe,
|
||||
.optional_payload_unsafe,
|
||||
.optional_payload_safe_ptr,
|
||||
|
||||
@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
||||
.typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
|
||||
.typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
|
||||
.optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
|
||||
.optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?),
|
||||
.optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
|
||||
.optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
|
||||
.optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),
|
||||
@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE
|
||||
return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
|
||||
}
|
||||
|
||||
fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const ptr = try resolveInst(mod, scope, inst.positionals.operand);
|
||||
const elem_ty = ptr.ty.elemType();
|
||||
|
||||
return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty));
|
||||
}
|
||||
|
||||
fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -132,4 +132,72 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exeUsingLlvmBackend("optionals", linux_x64);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\export fn main() c_int {
|
||||
\\ var opt_val: ?i32 = 10;
|
||||
\\ var null_val: ?i32 = null;
|
||||
\\
|
||||
\\ var val1: i32 = opt_val.?;
|
||||
\\ const val1_1: i32 = opt_val.?;
|
||||
\\ var ptr_val1 = &(opt_val.?);
|
||||
\\ const ptr_val1_1 = &(opt_val.?);
|
||||
\\
|
||||
\\ var val2: i32 = null_val orelse 20;
|
||||
\\ const val2_2: i32 = null_val orelse 20;
|
||||
\\
|
||||
\\ var value: i32 = 20;
|
||||
\\ var ptr_val2 = &(null_val orelse value);
|
||||
\\
|
||||
\\ const val3 = opt_val orelse 30;
|
||||
\\ var val3_var = opt_val orelse 30;
|
||||
\\
|
||||
\\ assert(val1 == 10);
|
||||
\\ assert(val1_1 == 10);
|
||||
\\ assert(ptr_val1.* == 10);
|
||||
\\ assert(ptr_val1_1.* == 10);
|
||||
\\
|
||||
\\ assert(val2 == 20);
|
||||
\\ assert(val2_2 == 20);
|
||||
\\ assert(ptr_val2.* == 20);
|
||||
\\
|
||||
\\ assert(val3 == 10);
|
||||
\\ assert(val3_var == 10);
|
||||
\\
|
||||
\\ (null_val orelse val2) = 1234;
|
||||
\\ assert(val2 == 1234);
|
||||
\\
|
||||
\\ (opt_val orelse val2) = 5678;
|
||||
\\ assert(opt_val.? == 5678);
|
||||
\\
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exeUsingLlvmBackend("for loop", linux_x64);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\export fn main() c_int {
|
||||
\\ var x: u32 = 0;
|
||||
\\ for ("hello") |_| {
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\ assert("hello".len == x);
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user