Skip to content

stage1: add @hasField() built-in #2438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -6867,6 +6867,16 @@ fn add(a: i32, b: i32) i32 { return a + b; }
It does not include functions, variables, or constants.
</p>
{#header_close#}
{#header_open|@hasField#}
<pre>{#syntax#}@hasField(comptime T: type, comptime name: []const u8) bool{#endsyntax#}</pre>
<p>Returns if the field name of a struct, union, or enum exists.</p>
<p>
The result is a compile time constant.
</p>
<p>
It does not include functions, variables, constants.
</p>
{#header_close#}
{#header_open|@memberType#}
<pre>{#syntax#}@memberType(comptime T: type, comptime index: usize) type{#endsyntax#}</pre>
<p>Returns the field type of a struct or union.</p>
Expand Down
10 changes: 10 additions & 0 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,7 @@ enum BuiltinFnId {
BuiltinFnIdMemberName,
BuiltinFnIdField,
BuiltinFnIdTypeInfo,
BuiltinFnIdHasField,
BuiltinFnIdTypeof,
BuiltinFnIdAddWithOverflow,
BuiltinFnIdSubWithOverflow,
Expand Down Expand Up @@ -2251,6 +2252,7 @@ enum IrInstructionId {
IrInstructionIdByteOffsetOf,
IrInstructionIdBitOffsetOf,
IrInstructionIdTypeInfo,
IrInstructionIdHasField,
IrInstructionIdTypeId,
IrInstructionIdSetEvalBranchQuota,
IrInstructionIdPtrType,
Expand Down Expand Up @@ -3229,6 +3231,14 @@ struct IrInstructionTypeInfo {
IrInstruction *type_value;
};

struct IrInstructionHasField {
IrInstruction base;

IrInstruction *container_type;
Buf *field_name_buffer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the existence of this field is correct. I think you can follow this pattern: 269a53b

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following another existing pattern.

IrInstruction *field_name_expr;
};

struct IrInstructionTypeId {
IrInstruction base;

Expand Down
2 changes: 2 additions & 0 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5530,6 +5530,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdByteOffsetOf:
case IrInstructionIdBitOffsetOf:
case IrInstructionIdTypeInfo:
case IrInstructionIdHasField:
case IrInstructionIdTypeId:
case IrInstructionIdSetEvalBranchQuota:
case IrInstructionIdPtrType:
Expand Down Expand Up @@ -7271,6 +7272,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);
create_builtin_fn(g, BuiltinFnIdField, "field", 2);
create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1);
create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2);
create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf
create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
Expand Down
70 changes: 70 additions & 0 deletions src/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) {
return IrInstructionIdTypeInfo;
}

static constexpr IrInstructionId ir_instruction_id(IrInstructionHasField *) {
return IrInstructionIdHasField;
}

static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) {
return IrInstructionIdTypeId;
}
Expand Down Expand Up @@ -1280,6 +1284,20 @@ static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *
return &instruction->base;
}

static IrInstruction *ir_build_has_field(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *container_type, IrInstruction *field_name_expr)
{
IrInstructionHasField *instruction = ir_build_instruction<IrInstructionHasField>(irb, scope, source_node);
instruction->container_type = container_type;
instruction->field_name_buffer = nullptr;
instruction->field_name_expr = field_name_expr;

ir_ref_instruction(container_type, irb->current_basic_block);
ir_ref_instruction(field_name_expr, irb->current_basic_block);

return &instruction->base;
}

static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *struct_ptr, TypeStructField *field)
{
Expand Down Expand Up @@ -4598,6 +4616,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo

return ir_build_load_ptr(irb, scope, node, ptr_instruction);
}
case BuiltinFnIdHasField:
{
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;

IrInstruction *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, type_info, lval);
}
case BuiltinFnIdTypeInfo:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
Expand Down Expand Up @@ -20578,6 +20611,40 @@ static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstr
}
}

static IrInstruction *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstructionHasField *instruction) {
Error err;
IrInstruction *container_type_value = instruction->container_type->child;
ZigType *container_type = ir_resolve_type(ira, container_type_value);
if (type_is_invalid(container_type))
return ira->codegen->invalid_instruction;

if ((err = ensure_complete_type(ira->codegen, container_type)))
return ira->codegen->invalid_instruction;

Buf *field_name = instruction->field_name_buffer;
if (!field_name) {
IrInstruction *field_name_expr = instruction->field_name_expr->child;
field_name = ir_resolve_str(ira, field_name_expr);
if (!field_name)
return ira->codegen->invalid_instruction;
}

bool result;
if (container_type->id == ZigTypeIdStruct)
result = (bool)find_struct_type_field(container_type, field_name);
else if (container_type->id == ZigTypeIdEnum)
result = (bool)find_enum_type_field(container_type, field_name);
else if (container_type->id == ZigTypeIdUnion)
result = (bool)find_union_type_field(container_type, field_name);
else {
ir_add_error(ira, container_type_value,
buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_build_const_bool(&ira->new_irb,
instruction->base.scope, instruction->base.source_node, result);
}

static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) {
IrInstruction *result = ir_build_breakpoint(&ira->new_irb,
instruction->base.scope, instruction->base.source_node);
Expand Down Expand Up @@ -23068,6 +23135,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction);
case IrInstructionIdTypeInfo:
return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction);
case IrInstructionIdHasField:
return ir_analyze_instruction_has_field(ira, (IrInstructionHasField *) instruction);
case IrInstructionIdTypeId:
return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction);
case IrInstructionIdSetEvalBranchQuota:
Expand Down Expand Up @@ -23355,6 +23424,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdByteOffsetOf:
case IrInstructionIdBitOffsetOf:
case IrInstructionIdTypeInfo:
case IrInstructionIdHasField:
case IrInstructionIdTypeId:
case IrInstructionIdAlignCast:
case IrInstructionIdOpaqueType:
Expand Down
11 changes: 11 additions & 0 deletions src/ir_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,14 @@ static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction)
fprintf(irp->f, ")");
}

static void ir_print_has_field(IrPrint *irp, IrInstructionHasField *instruction) {
fprintf(irp->f, "@hasField(");
ir_print_other_instruction(irp, instruction->container_type);
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->field_name_expr);
fprintf(irp->f, ")");
}

static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) {
fprintf(irp->f, "@typeId(");
ir_print_other_instruction(irp, instruction->type_value);
Expand Down Expand Up @@ -1757,6 +1765,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTypeInfo:
ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction);
break;
case IrInstructionIdHasField:
ir_print_has_field(irp, (IrInstructionHasField *)instruction);
break;
case IrInstructionIdTypeId:
ir_print_type_id(irp, (IrInstructionTypeId *)instruction);
break;
Expand Down
1 change: 1 addition & 0 deletions test/stage1/behavior.zig
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,5 @@ comptime {
_ = @import("behavior/void.zig");
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
_ = @import("behavior/hasfield.zig");
}
30 changes: 30 additions & 0 deletions test/stage1/behavior/hasfield.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const expect = @import("std").testing.expect;
const builtin = @import("builtin");

test "@hasField" {
const struc = struct {
a: i32,
b: []u8,
};
expect(@hasField(struc, "a") == true);
expect(@hasField(struc, "b") == true);
expect(@hasField(struc, "non-existant") == false);

const unin = union {
a: u64,
b: []u16,
};
expect(@hasField(unin, "a") == true);
expect(@hasField(unin, "b") == true);
expect(@hasField(unin, "non-existant") == false);

const enm = enum {
a,
b,
};
expect(@hasField(enm, "a") == true);
expect(@hasField(enm, "b") == true);
expect(@hasField(enm, "non-existant") == false);

expect(@hasField(builtin, "os") == true);
}