Merge pull request #338 from raulgrell/master

Add `@offsetOf` builtin function
master
Andrew Kelley 2017-04-20 10:45:23 -04:00 committed by GitHub
commit 68f75a3130
7 changed files with 138 additions and 3 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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:
{ {

View File

@ -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");
} }

View File

@ -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();

View File

@ -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"));
}