commit
68f75a3130
|
@ -314,6 +314,10 @@ for the current target.
|
||||||
|
|
||||||
The result is a target-specific compile time constant.
|
The result is a target-specific compile time constant.
|
||||||
|
|
||||||
|
### @offsetOf(comptime T: type, comptime field_name: [] const u8) -> (number literal)
|
||||||
|
|
||||||
|
This function returns the byte offset of a field relative to its containing struct.
|
||||||
|
|
||||||
### Overflow Arithmetic
|
### Overflow Arithmetic
|
||||||
|
|
||||||
These functions take an integer type, two variables of the specified type,
|
These functions take an integer type, two variables of the specified type,
|
||||||
|
|
|
@ -1190,6 +1190,7 @@ enum BuiltinFnId {
|
||||||
BuiltinFnIdIntToPtr,
|
BuiltinFnIdIntToPtr,
|
||||||
BuiltinFnIdEnumTagName,
|
BuiltinFnIdEnumTagName,
|
||||||
BuiltinFnIdFieldParentPtr,
|
BuiltinFnIdFieldParentPtr,
|
||||||
|
BuiltinFnIdOffsetOf,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuiltinFnEntry {
|
struct BuiltinFnEntry {
|
||||||
|
@ -1742,6 +1743,7 @@ enum IrInstructionId {
|
||||||
IrInstructionIdEnumTagName,
|
IrInstructionIdEnumTagName,
|
||||||
IrInstructionIdSetFnRefInline,
|
IrInstructionIdSetFnRefInline,
|
||||||
IrInstructionIdFieldParentPtr,
|
IrInstructionIdFieldParentPtr,
|
||||||
|
IrInstructionIdOffsetOf,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IrInstruction {
|
struct IrInstruction {
|
||||||
|
@ -2503,6 +2505,13 @@ struct IrInstructionFieldParentPtr {
|
||||||
TypeStructField *field;
|
TypeStructField *field;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionOffsetOf {
|
||||||
|
IrInstruction base;
|
||||||
|
|
||||||
|
IrInstruction *type_value;
|
||||||
|
IrInstruction *field_name;
|
||||||
|
};
|
||||||
|
|
||||||
static const size_t slice_ptr_index = 0;
|
static const size_t slice_ptr_index = 0;
|
||||||
static const size_t slice_len_index = 1;
|
static const size_t slice_len_index = 1;
|
||||||
|
|
||||||
|
|
|
@ -2888,6 +2888,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||||
case IrInstructionIdDeclRef:
|
case IrInstructionIdDeclRef:
|
||||||
case IrInstructionIdSwitchVar:
|
case IrInstructionIdSwitchVar:
|
||||||
case IrInstructionIdSetFnRefInline:
|
case IrInstructionIdSetFnRefInline:
|
||||||
|
case IrInstructionIdOffsetOf:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
case IrInstructionIdReturn:
|
case IrInstructionIdReturn:
|
||||||
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
||||||
|
@ -4548,6 +4549,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||||
create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2);
|
create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2);
|
||||||
create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);
|
create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);
|
||||||
create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);
|
create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);
|
||||||
|
create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_compile_var(CodeGen *g, const char *name, ConstExprValue *value) {
|
static void add_compile_var(CodeGen *g, const char *name, ConstExprValue *value) {
|
||||||
|
|
78
src/ir.cpp
78
src/ir.cpp
|
@ -553,6 +553,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *
|
||||||
return IrInstructionIdFieldParentPtr;
|
return IrInstructionIdFieldParentPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) {
|
||||||
|
return IrInstructionIdOffsetOf;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||||
T *special_instruction = allocate<T>(1);
|
T *special_instruction = allocate<T>(1);
|
||||||
|
@ -2183,6 +2187,19 @@ static IrInstruction *ir_build_field_parent_ptr(IrBuilder *irb, Scope *scope, As
|
||||||
return &instruction->base;
|
return &instruction->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
|
IrInstruction *type_value, IrInstruction *field_name)
|
||||||
|
{
|
||||||
|
IrInstructionOffsetOf *instruction = ir_build_instruction<IrInstructionOffsetOf>(irb, scope, source_node);
|
||||||
|
instruction->type_value = type_value;
|
||||||
|
instruction->field_name = field_name;
|
||||||
|
|
||||||
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
||||||
|
ir_ref_instruction(field_name, irb->current_basic_block);
|
||||||
|
|
||||||
|
return &instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
|
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -2863,6 +2880,13 @@ static IrInstruction *ir_instruction_fieldparentptr_get_dep(IrInstructionFieldPa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_instruction_offsetof_get_dep(IrInstructionOffsetOf *instruction, size_t index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0: return instruction->type_value;
|
||||||
|
case 1: return instruction->field_name;
|
||||||
|
default: return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
|
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
|
@ -3058,6 +3082,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||||
return ir_instruction_setfnrefinline_get_dep((IrInstructionSetFnRefInline *) instruction, index);
|
return ir_instruction_setfnrefinline_get_dep((IrInstructionSetFnRefInline *) instruction, index);
|
||||||
case IrInstructionIdFieldParentPtr:
|
case IrInstructionIdFieldParentPtr:
|
||||||
return ir_instruction_fieldparentptr_get_dep((IrInstructionFieldParentPtr *) instruction, index);
|
return ir_instruction_fieldparentptr_get_dep((IrInstructionFieldParentPtr *) instruction, index);
|
||||||
|
case IrInstructionIdOffsetOf:
|
||||||
|
return ir_instruction_offsetof_get_dep((IrInstructionOffsetOf *) instruction, index);
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -4384,6 +4410,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||||
|
|
||||||
return ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
|
return ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
|
||||||
}
|
}
|
||||||
|
case BuiltinFnIdOffsetOf:
|
||||||
|
{
|
||||||
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||||
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||||
|
if (arg0_value == irb->codegen->invalid_instruction)
|
||||||
|
return arg0_value;
|
||||||
|
|
||||||
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||||
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
||||||
|
if (arg1_value == irb->codegen->invalid_instruction)
|
||||||
|
return arg1_value;
|
||||||
|
|
||||||
|
return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -11401,6 +11441,41 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
|
||||||
return result_type;
|
return result_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,
|
||||||
|
IrInstructionOffsetOf *instruction)
|
||||||
|
{
|
||||||
|
IrInstruction *type_value = instruction->type_value->other;
|
||||||
|
TypeTableEntry *container_type = ir_resolve_type(ira, type_value);
|
||||||
|
if (type_is_invalid(container_type))
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
ensure_complete_type(ira->codegen, container_type);
|
||||||
|
|
||||||
|
IrInstruction *field_name_value = instruction->field_name->other;
|
||||||
|
Buf *field_name = ir_resolve_str(ira, field_name_value);
|
||||||
|
if (!field_name)
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
|
||||||
|
if (container_type->id != TypeTableEntryIdStruct) {
|
||||||
|
ir_add_error(ira, type_value,
|
||||||
|
buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name)));
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeStructField *field = find_struct_type_field(container_type, field_name);
|
||||||
|
if (field == nullptr) {
|
||||||
|
ir_add_error(ira, field_name_value,
|
||||||
|
buf_sprintf("struct '%s' has no field '%s'",
|
||||||
|
buf_ptr(&container_type->name), buf_ptr(field_name)));
|
||||||
|
return ira->codegen->builtin_types.entry_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index);
|
||||||
|
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
|
||||||
|
bignum_init_unsigned(&out_val->data.x_bignum, byte_offset);
|
||||||
|
return ira->codegen->builtin_types.entry_num_lit_int;
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
|
static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
|
||||||
IrInstruction *type_value = instruction->type_value->other;
|
IrInstruction *type_value = instruction->type_value->other;
|
||||||
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
|
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
|
||||||
|
@ -12921,6 +12996,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||||
return ir_analyze_instruction_set_fn_ref_inline(ira, (IrInstructionSetFnRefInline *)instruction);
|
return ir_analyze_instruction_set_fn_ref_inline(ira, (IrInstructionSetFnRefInline *)instruction);
|
||||||
case IrInstructionIdFieldParentPtr:
|
case IrInstructionIdFieldParentPtr:
|
||||||
return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);
|
return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);
|
||||||
|
case IrInstructionIdOffsetOf:
|
||||||
|
return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction);
|
||||||
case IrInstructionIdMaybeWrap:
|
case IrInstructionIdMaybeWrap:
|
||||||
case IrInstructionIdErrWrapCode:
|
case IrInstructionIdErrWrapCode:
|
||||||
case IrInstructionIdErrWrapPayload:
|
case IrInstructionIdErrWrapPayload:
|
||||||
|
@ -13108,6 +13185,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||||
case IrInstructionIdEnumTagName:
|
case IrInstructionIdEnumTagName:
|
||||||
case IrInstructionIdSetFnRefInline:
|
case IrInstructionIdSetFnRefInline:
|
||||||
case IrInstructionIdFieldParentPtr:
|
case IrInstructionIdFieldParentPtr:
|
||||||
|
case IrInstructionIdOffsetOf:
|
||||||
return false;
|
return false;
|
||||||
case IrInstructionIdAsm:
|
case IrInstructionIdAsm:
|
||||||
{
|
{
|
||||||
|
|
|
@ -885,6 +885,14 @@ static void ir_print_field_parent_ptr(IrPrint *irp, IrInstructionFieldParentPtr
|
||||||
fprintf(irp->f, ")");
|
fprintf(irp->f, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction) {
|
||||||
|
fprintf(irp->f, "@offset_of(");
|
||||||
|
ir_print_other_instruction(irp, instruction->type_value);
|
||||||
|
fprintf(irp->f, ",");
|
||||||
|
ir_print_other_instruction(irp, instruction->field_name);
|
||||||
|
fprintf(irp->f, ")");
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
ir_print_prefix(irp, instruction);
|
ir_print_prefix(irp, instruction);
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
|
@ -1175,6 +1183,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
case IrInstructionIdFieldParentPtr:
|
case IrInstructionIdFieldParentPtr:
|
||||||
ir_print_field_parent_ptr(irp, (IrInstructionFieldParentPtr *)instruction);
|
ir_print_field_parent_ptr(irp, (IrInstructionFieldParentPtr *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdOffsetOf:
|
||||||
|
ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fprintf(irp->f, "\n");
|
fprintf(irp->f, "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,15 +90,20 @@ fn castToMaybeTypeError(z: i32) {
|
||||||
const f = z;
|
const f = z;
|
||||||
const g: %?i32 = f;
|
const g: %?i32 = f;
|
||||||
|
|
||||||
const a = A{ .a = 1 };
|
const a = A{ .a = z };
|
||||||
const b: %?A = a;
|
const b: %?A = a;
|
||||||
assert((??%%b).a == 1);
|
assert((??%%b).a == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "implicitly cast from int to %?T" {
|
test "implicitly cast from int to %?T" {
|
||||||
const f: %?i32 = 1;
|
implicitIntLitToMaybe();
|
||||||
comptime const g: %?i32 = 1;
|
comptime implicitIntLitToMaybe();
|
||||||
}
|
}
|
||||||
|
fn implicitIntLitToMaybe() {
|
||||||
|
const f: ?i32 = 1;
|
||||||
|
const g: %?i32 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
test "return null from fn() -> %?&T" {
|
test "return null from fn() -> %?&T" {
|
||||||
const a = returnNullFromMaybeTypeErrorRef();
|
const a = returnNullFromMaybeTypeErrorRef();
|
||||||
|
|
|
@ -6,3 +6,29 @@ test "sizeofAndTypeOf" {
|
||||||
}
|
}
|
||||||
const x: u16 = 13;
|
const x: u16 = 13;
|
||||||
const z: @typeOf(x) = 19;
|
const z: @typeOf(x) = 19;
|
||||||
|
|
||||||
|
const A = struct {
|
||||||
|
a: u8,
|
||||||
|
b: u32,
|
||||||
|
c: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const P = packed struct {
|
||||||
|
a: u8,
|
||||||
|
b: u32,
|
||||||
|
c: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
test "offsetOf" {
|
||||||
|
// Packed structs have fixed memory layout
|
||||||
|
const p: P = undefined;
|
||||||
|
assert(@offsetOf(P, "a") == 0);
|
||||||
|
assert(@offsetOf(@typeOf(p), "b") == 1);
|
||||||
|
assert(@offsetOf(@typeOf(p), "c") == 5);
|
||||||
|
|
||||||
|
// Non-packed struct fields can be moved/padded
|
||||||
|
const a: A = undefined;
|
||||||
|
assert(usize(&a.a) - usize(&a) == @offsetOf(A, "a"));
|
||||||
|
assert(usize(&a.b) - usize(&a) == @offsetOf(@typeOf(a), "b"));
|
||||||
|
assert(usize(&a.c) - usize(&a) == @offsetOf(@typeOf(a), "c"));
|
||||||
|
}
|
Loading…
Reference in New Issue