parent
21ed939117
commit
269a53b6af
@ -6734,6 +6734,38 @@ export fn @"A function name that is a complete sentence."() void {}
|
|||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
|
{#header_open|@hasDecl#}
|
||||||
|
<pre>{#syntax#}@hasDecl(comptime container: type, comptime name: []const u8) bool{#endsyntax#}</pre>
|
||||||
|
<p>
|
||||||
|
Returns whether or not a {#link|struct#}, {#link|enum#}, or {#link|union#} has a declaration
|
||||||
|
matching {#syntax#}name{#endsyntax#}.
|
||||||
|
</p>
|
||||||
|
{#code_begin|test#}
|
||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
|
const Foo = struct {
|
||||||
|
nope: i32,
|
||||||
|
|
||||||
|
pub var blah = "xxx";
|
||||||
|
const hi = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
test "@hasDecl" {
|
||||||
|
assert(@hasDecl(Foo, "blah"));
|
||||||
|
|
||||||
|
// Even though `hi` is private, @hasDecl returns true because this test is
|
||||||
|
// in the same file scope as Foo. It would return false if Foo was declared
|
||||||
|
// in a different file.
|
||||||
|
assert(@hasDecl(Foo, "hi"));
|
||||||
|
|
||||||
|
// @hasDecl is for declarations; not fields.
|
||||||
|
assert(!@hasDecl(Foo, "nope"));
|
||||||
|
assert(!@hasDecl(Foo, "nope1234"));
|
||||||
|
}
|
||||||
|
{#code_end#}
|
||||||
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@import#}
|
{#header_open|@import#}
|
||||||
<pre>{#syntax#}@import(comptime path: []u8) type{#endsyntax#}</pre>
|
<pre>{#syntax#}@import(comptime path: []u8) type{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
|
@ -1471,6 +1471,7 @@ enum BuiltinFnId {
|
|||||||
BuiltinFnIdErrorReturnTrace,
|
BuiltinFnIdErrorReturnTrace,
|
||||||
BuiltinFnIdAtomicRmw,
|
BuiltinFnIdAtomicRmw,
|
||||||
BuiltinFnIdAtomicLoad,
|
BuiltinFnIdAtomicLoad,
|
||||||
|
BuiltinFnIdHasDecl,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuiltinFnEntry {
|
struct BuiltinFnEntry {
|
||||||
@ -2297,6 +2298,7 @@ enum IrInstructionId {
|
|||||||
IrInstructionIdArrayToVector,
|
IrInstructionIdArrayToVector,
|
||||||
IrInstructionIdAssertZero,
|
IrInstructionIdAssertZero,
|
||||||
IrInstructionIdAssertNonNull,
|
IrInstructionIdAssertNonNull,
|
||||||
|
IrInstructionIdHasDecl,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IrInstruction {
|
struct IrInstruction {
|
||||||
@ -3503,6 +3505,13 @@ struct IrInstructionAssertNonNull {
|
|||||||
IrInstruction *target;
|
IrInstruction *target;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionHasDecl {
|
||||||
|
IrInstruction base;
|
||||||
|
|
||||||
|
IrInstruction *container;
|
||||||
|
IrInstruction *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;
|
||||||
|
|
||||||
|
@ -5616,6 +5616,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||||||
case IrInstructionIdLoadPtr:
|
case IrInstructionIdLoadPtr:
|
||||||
case IrInstructionIdBitCast:
|
case IrInstructionIdBitCast:
|
||||||
case IrInstructionIdGlobalAsm:
|
case IrInstructionIdGlobalAsm:
|
||||||
|
case IrInstructionIdHasDecl:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
|
|
||||||
case IrInstructionIdDeclVarGen:
|
case IrInstructionIdDeclVarGen:
|
||||||
@ -7409,6 +7410,7 @@ static void define_builtin_fns(CodeGen *g) {
|
|||||||
create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1);
|
create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1);
|
||||||
create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2);
|
create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2);
|
||||||
create_builtin_fn(g, BuiltinFnIdThis, "This", 0);
|
create_builtin_fn(g, BuiltinFnIdThis, "This", 0);
|
||||||
|
create_builtin_fn(g, BuiltinFnIdHasDecl, "hasDecl", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *bool_to_str(bool b) {
|
static const char *bool_to_str(bool b) {
|
||||||
|
62
src/ir.cpp
62
src/ir.cpp
@ -1011,6 +1011,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *)
|
|||||||
return IrInstructionIdAssertNonNull;
|
return IrInstructionIdAssertNonNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionHasDecl *) {
|
||||||
|
return IrInstructionIdHasDecl;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -3014,6 +3018,19 @@ static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *sourc
|
|||||||
return &instruction->base;
|
return &instruction->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
|
IrInstruction *container, IrInstruction *name)
|
||||||
|
{
|
||||||
|
IrInstructionHasDecl *instruction = ir_build_instruction<IrInstructionHasDecl>(irb, scope, source_node);
|
||||||
|
instruction->container = container;
|
||||||
|
instruction->name = name;
|
||||||
|
|
||||||
|
ir_ref_instruction(container, irb->current_basic_block);
|
||||||
|
ir_ref_instruction(name, irb->current_basic_block);
|
||||||
|
|
||||||
|
return &instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) {
|
static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) {
|
||||||
IrInstructionCheckRuntimeScope *instruction = ir_build_instruction<IrInstructionCheckRuntimeScope>(irb, scope, source_node);
|
IrInstructionCheckRuntimeScope *instruction = ir_build_instruction<IrInstructionCheckRuntimeScope>(irb, scope, source_node);
|
||||||
instruction->scope_is_comptime = scope_is_comptime;
|
instruction->scope_is_comptime = scope_is_comptime;
|
||||||
@ -5098,6 +5115,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||||||
}
|
}
|
||||||
return ir_lval_wrap(irb, scope, result, lval);
|
return ir_lval_wrap(irb, scope, result, lval);
|
||||||
}
|
}
|
||||||
|
case BuiltinFnIdHasDecl:
|
||||||
|
{
|
||||||
|
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 *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
|
||||||
|
return ir_lval_wrap(irb, scope, has_decl, lval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -23173,6 +23205,33 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira,
|
|||||||
return ir_const_void(ira, &instruction->base);
|
return ir_const_void(ira, &instruction->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstructionHasDecl *instruction) {
|
||||||
|
ZigType *container_type = ir_resolve_type(ira, instruction->container->child);
|
||||||
|
if (type_is_invalid(container_type))
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
|
||||||
|
Buf *name = ir_resolve_str(ira, instruction->name->child);
|
||||||
|
if (name == nullptr)
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
|
||||||
|
if (!is_container(container_type)) {
|
||||||
|
ir_add_error(ira, instruction->container,
|
||||||
|
buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name)));
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeDecls *container_scope = get_container_scope(container_type);
|
||||||
|
Tld *tld = find_container_decl(ira->codegen, container_scope, name);
|
||||||
|
if (tld == nullptr)
|
||||||
|
return ir_const_bool(ira, &instruction->base, false);
|
||||||
|
|
||||||
|
if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.scope)) {
|
||||||
|
return ir_const_bool(ira, &instruction->base, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ir_const_bool(ira, &instruction->base, true);
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
case IrInstructionIdInvalid:
|
case IrInstructionIdInvalid:
|
||||||
@ -23467,6 +23526,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
|
|||||||
return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction);
|
return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction);
|
||||||
case IrInstructionIdCheckRuntimeScope:
|
case IrInstructionIdCheckRuntimeScope:
|
||||||
return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction);
|
return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction);
|
||||||
|
case IrInstructionIdHasDecl:
|
||||||
|
return ir_analyze_instruction_has_decl(ira, (IrInstructionHasDecl *)instruction);
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -23703,6 +23764,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||||||
case IrInstructionIdEnumToInt:
|
case IrInstructionIdEnumToInt:
|
||||||
case IrInstructionIdVectorToArray:
|
case IrInstructionIdVectorToArray:
|
||||||
case IrInstructionIdArrayToVector:
|
case IrInstructionIdArrayToVector:
|
||||||
|
case IrInstructionIdHasDecl:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case IrInstructionIdAsm:
|
case IrInstructionIdAsm:
|
||||||
|
@ -1453,6 +1453,14 @@ static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_has_decl(IrPrint *irp, IrInstructionHasDecl *instruction) {
|
||||||
|
fprintf(irp->f, "@hasDecl(");
|
||||||
|
ir_print_other_instruction(irp, instruction->container);
|
||||||
|
fprintf(irp->f, ",");
|
||||||
|
ir_print_other_instruction(irp, instruction->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) {
|
||||||
@ -1920,6 +1928,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||||||
case IrInstructionIdResizeSlice:
|
case IrInstructionIdResizeSlice:
|
||||||
ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction);
|
ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdHasDecl:
|
||||||
|
ir_print_has_decl(irp, (IrInstructionHasDecl *)instruction);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fprintf(irp->f, "\n");
|
fprintf(irp->f, "\n");
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,15 @@ const tests = @import("tests.zig");
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
|
cases.add(
|
||||||
|
"@hasDecl with non-container",
|
||||||
|
\\export fn entry() void {
|
||||||
|
\\ _ = @hasDecl(i32, "hi");
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"tmp.zig:2:18: error: expected struct, enum, or union; found 'i32'",
|
||||||
|
);
|
||||||
|
|
||||||
cases.add(
|
cases.add(
|
||||||
"field access of slices",
|
"field access of slices",
|
||||||
\\export fn entry() void {
|
\\export fn entry() void {
|
||||||
|
@ -53,6 +53,7 @@ comptime {
|
|||||||
_ = @import("behavior/fn_in_struct_in_comptime.zig");
|
_ = @import("behavior/fn_in_struct_in_comptime.zig");
|
||||||
_ = @import("behavior/for.zig");
|
_ = @import("behavior/for.zig");
|
||||||
_ = @import("behavior/generics.zig");
|
_ = @import("behavior/generics.zig");
|
||||||
|
_ = @import("behavior/hasdecl.zig");
|
||||||
_ = @import("behavior/if.zig");
|
_ = @import("behavior/if.zig");
|
||||||
_ = @import("behavior/import.zig");
|
_ = @import("behavior/import.zig");
|
||||||
_ = @import("behavior/incomplete_struct_param_tld.zig");
|
_ = @import("behavior/incomplete_struct_param_tld.zig");
|
||||||
|
21
test/stage1/behavior/hasdecl.zig
Normal file
21
test/stage1/behavior/hasdecl.zig
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
|
const Foo = @import("hasdecl/foo.zig");
|
||||||
|
|
||||||
|
const Bar = struct {
|
||||||
|
nope: i32,
|
||||||
|
|
||||||
|
const hi = 1;
|
||||||
|
pub var blah = "xxx";
|
||||||
|
};
|
||||||
|
|
||||||
|
test "@hasDecl" {
|
||||||
|
expect(@hasDecl(Foo, "public_thing"));
|
||||||
|
expect(!@hasDecl(Foo, "private_thing"));
|
||||||
|
expect(!@hasDecl(Foo, "no_thing"));
|
||||||
|
|
||||||
|
expect(@hasDecl(Bar, "hi"));
|
||||||
|
expect(@hasDecl(Bar, "blah"));
|
||||||
|
expect(!@hasDecl(Bar, "nope"));
|
||||||
|
}
|
2
test/stage1/behavior/hasdecl/foo.zig
Normal file
2
test/stage1/behavior/hasdecl/foo.zig
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub const public_thing = 42;
|
||||||
|
const private_thing = 666;
|
Loading…
x
Reference in New Issue
Block a user