implement null terminated pointers
This commit is contained in:
parent
e3404e3c78
commit
1aa978f32e
@ -144,6 +144,7 @@ pub const TypeInfo = union(enum) {
|
|||||||
alignment: comptime_int,
|
alignment: comptime_int,
|
||||||
child: type,
|
child: type,
|
||||||
is_allowzero: bool,
|
is_allowzero: bool,
|
||||||
|
is_null_terminated: bool,
|
||||||
|
|
||||||
/// This data structure is used by the Zig language code generation and
|
/// This data structure is used by the Zig language code generation and
|
||||||
/// therefore must be kept in sync with the compiler implementation.
|
/// therefore must be kept in sync with the compiler implementation.
|
||||||
|
@ -558,3 +558,4 @@ pub fn refAllDecls(comptime T: type) void {
|
|||||||
if (!builtin.is_test) return;
|
if (!builtin.is_test) return;
|
||||||
_ = declarations(T);
|
_ = declarations(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ enum PtrLen {
|
|||||||
PtrLenUnknown,
|
PtrLenUnknown,
|
||||||
PtrLenSingle,
|
PtrLenSingle,
|
||||||
PtrLenC,
|
PtrLenC,
|
||||||
|
PtrLenNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This one corresponds to the builtin.zig enum.
|
// This one corresponds to the builtin.zig enum.
|
||||||
@ -825,6 +826,7 @@ struct AstNodePointerType {
|
|||||||
Token *allow_zero_token;
|
Token *allow_zero_token;
|
||||||
bool is_const;
|
bool is_const;
|
||||||
bool is_volatile;
|
bool is_volatile;
|
||||||
|
bool is_null_terminated;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodeInferredArrayType {
|
struct AstNodeInferredArrayType {
|
||||||
@ -838,6 +840,7 @@ struct AstNodeArrayType {
|
|||||||
Token *allow_zero_token;
|
Token *allow_zero_token;
|
||||||
bool is_const;
|
bool is_const;
|
||||||
bool is_volatile;
|
bool is_volatile;
|
||||||
|
bool is_null_terminated;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodeUsingNamespace {
|
struct AstNodeUsingNamespace {
|
||||||
|
@ -460,6 +460,8 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) {
|
|||||||
return "[*]";
|
return "[*]";
|
||||||
case PtrLenC:
|
case PtrLenC:
|
||||||
return "[*c]";
|
return "[*c]";
|
||||||
|
case PtrLenNull:
|
||||||
|
return "[*]null ";
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
@ -7032,7 +7034,7 @@ uint32_t type_id_hash(TypeId x) {
|
|||||||
return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
|
return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
|
||||||
case ZigTypeIdPointer:
|
case ZigTypeIdPointer:
|
||||||
return hash_ptr(x.data.pointer.child_type) +
|
return hash_ptr(x.data.pointer.child_type) +
|
||||||
((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
|
(uint32_t)x.data.pointer.ptr_len * 1120226602u +
|
||||||
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
|
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
|
||||||
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
|
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
|
||||||
(x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
|
(x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
|
||||||
|
@ -992,6 +992,10 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
|
|||||||
jw_object_field(jw, "len");
|
jw_object_field(jw, "len");
|
||||||
jw_int(jw, 3);
|
jw_int(jw, 3);
|
||||||
break;
|
break;
|
||||||
|
case PtrLenNull:
|
||||||
|
jw_object_field(jw, "len");
|
||||||
|
jw_int(jw, 4);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
anal_dump_pointer_attrs(ctx, ty);
|
anal_dump_pointer_attrs(ctx, ty);
|
||||||
break;
|
break;
|
||||||
|
27
src/ir.cpp
27
src/ir.cpp
@ -6043,13 +6043,25 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) {
|
|||||||
|
|
||||||
static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||||
assert(node->type == NodeTypePointerType);
|
assert(node->type == NodeTypePointerType);
|
||||||
|
|
||||||
PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
|
PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
|
||||||
|
if (node->data.pointer_type.is_null_terminated) {
|
||||||
|
if (ptr_len == PtrLenUnknown) {
|
||||||
|
ptr_len = PtrLenNull;
|
||||||
|
} else {
|
||||||
|
exec_add_error_node(irb->codegen, irb->exec, node,
|
||||||
|
buf_sprintf("null-terminated pointer must be specified with [*] token"));
|
||||||
|
return irb->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool is_const = node->data.pointer_type.is_const;
|
bool is_const = node->data.pointer_type.is_const;
|
||||||
bool is_volatile = node->data.pointer_type.is_volatile;
|
bool is_volatile = node->data.pointer_type.is_volatile;
|
||||||
bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;
|
bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;
|
||||||
AstNode *expr_node = node->data.pointer_type.op_expr;
|
AstNode *expr_node = node->data.pointer_type.op_expr;
|
||||||
AstNode *align_expr = node->data.pointer_type.align_expr;
|
AstNode *align_expr = node->data.pointer_type.align_expr;
|
||||||
|
|
||||||
|
|
||||||
IrInstruction *align_value;
|
IrInstruction *align_value;
|
||||||
if (align_expr != nullptr) {
|
if (align_expr != nullptr) {
|
||||||
align_value = ir_gen_node(irb, align_expr, scope);
|
align_value = ir_gen_node(irb, align_expr, scope);
|
||||||
@ -9793,6 +9805,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
|
|||||||
// alignment can be decreased
|
// alignment can be decreased
|
||||||
// bit offset attributes must match exactly
|
// bit offset attributes must match exactly
|
||||||
// PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
|
// PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
|
||||||
|
// PtrLenNull can coerce into PtrLenUnknown
|
||||||
ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
|
ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
|
||||||
ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
|
ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
|
||||||
bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
|
bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
|
||||||
@ -9843,7 +9856,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
|
bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
|
||||||
if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) &&
|
bool ok_null_term_ptrs =
|
||||||
|
actual_ptr_type->data.pointer.ptr_len == PtrLenNull ||
|
||||||
|
wanted_ptr_type->data.pointer.ptr_len == PtrLenUnknown;
|
||||||
|
if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr || ok_null_term_ptrs) &&
|
||||||
type_has_bits(wanted_type) == type_has_bits(actual_type) &&
|
type_has_bits(wanted_type) == type_has_bits(actual_type) &&
|
||||||
(!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
|
(!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
|
||||||
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
|
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
|
||||||
@ -14532,6 +14548,7 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
|
|||||||
case PtrLenSingle:
|
case PtrLenSingle:
|
||||||
return false;
|
return false;
|
||||||
case PtrLenUnknown:
|
case PtrLenUnknown:
|
||||||
|
case PtrLenNull:
|
||||||
case PtrLenC:
|
case PtrLenC:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -21166,6 +21183,7 @@ static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) {
|
|||||||
case PtrLenSingle:
|
case PtrLenSingle:
|
||||||
return BuiltinPtrSizeOne;
|
return BuiltinPtrSizeOne;
|
||||||
case PtrLenUnknown:
|
case PtrLenUnknown:
|
||||||
|
case PtrLenNull:
|
||||||
return BuiltinPtrSizeMany;
|
return BuiltinPtrSizeMany;
|
||||||
case PtrLenC:
|
case PtrLenC:
|
||||||
return BuiltinPtrSizeC;
|
return BuiltinPtrSizeC;
|
||||||
@ -21210,7 +21228,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
|
|||||||
result->special = ConstValSpecialStatic;
|
result->special = ConstValSpecialStatic;
|
||||||
result->type = type_info_pointer_type;
|
result->type = type_info_pointer_type;
|
||||||
|
|
||||||
ConstExprValue **fields = alloc_const_vals_ptrs(6);
|
ConstExprValue **fields = alloc_const_vals_ptrs(7);
|
||||||
result->data.x_struct.fields = fields;
|
result->data.x_struct.fields = fields;
|
||||||
|
|
||||||
// size: Size
|
// size: Size
|
||||||
@ -21246,6 +21264,11 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
|
|||||||
fields[5]->special = ConstValSpecialStatic;
|
fields[5]->special = ConstValSpecialStatic;
|
||||||
fields[5]->type = ira->codegen->builtin_types.entry_bool;
|
fields[5]->type = ira->codegen->builtin_types.entry_bool;
|
||||||
fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
|
fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
|
||||||
|
// is_null_terminated: bool
|
||||||
|
ensure_field_index(result->type, "is_null_terminated", 6);
|
||||||
|
fields[6]->special = ConstValSpecialStatic;
|
||||||
|
fields[6]->type = ira->codegen->builtin_types.entry_bool;
|
||||||
|
fields[6]->data.x_bool = attrs_type->data.pointer.ptr_len == PtrLenNull;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -2618,6 +2618,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
|
|||||||
if (array != nullptr) {
|
if (array != nullptr) {
|
||||||
assert(array->type == NodeTypeArrayType);
|
assert(array->type == NodeTypeArrayType);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (eat_token_if(pc, TokenIdKeywordNull) != nullptr) {
|
||||||
|
array->data.array_type.is_null_terminated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
|
Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
|
||||||
if (allowzero_token != nullptr) {
|
if (allowzero_token != nullptr) {
|
||||||
array->data.array_type.allow_zero_token = allowzero_token;
|
array->data.array_type.allow_zero_token = allowzero_token;
|
||||||
@ -2653,6 +2658,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
|
|||||||
if (child == nullptr)
|
if (child == nullptr)
|
||||||
child = ptr;
|
child = ptr;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (eat_token_if(pc, TokenIdKeywordNull) != nullptr) {
|
||||||
|
child->data.pointer_type.is_null_terminated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
|
Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
|
||||||
if (allowzero_token != nullptr) {
|
if (allowzero_token != nullptr) {
|
||||||
child->data.pointer_type.allow_zero_token = allowzero_token;
|
child->data.pointer_type.allow_zero_token = allowzero_token;
|
||||||
|
@ -291,6 +291,7 @@ static TokenId ptr_len_to_token_id(PtrLen ptr_len) {
|
|||||||
case PtrLenSingle:
|
case PtrLenSingle:
|
||||||
return TokenIdStar;
|
return TokenIdStar;
|
||||||
case PtrLenUnknown:
|
case PtrLenUnknown:
|
||||||
|
case PtrLenNull:
|
||||||
return TokenIdBracketStarBracket;
|
return TokenIdBracketStarBracket;
|
||||||
case PtrLenC:
|
case PtrLenC:
|
||||||
return TokenIdBracketStarCBracket;
|
return TokenIdBracketStarCBracket;
|
||||||
@ -302,6 +303,7 @@ static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_vo
|
|||||||
AstNode *node = trans_create_node(c, NodeTypePointerType);
|
AstNode *node = trans_create_node(c, NodeTypePointerType);
|
||||||
node->data.pointer_type.star_token = allocate<ZigToken>(1);
|
node->data.pointer_type.star_token = allocate<ZigToken>(1);
|
||||||
node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len);
|
node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len);
|
||||||
|
node->data.pointer_type.is_null_terminated = (ptr_len == PtrLenNull);
|
||||||
node->data.pointer_type.is_const = is_const;
|
node->data.pointer_type.is_const = is_const;
|
||||||
node->data.pointer_type.is_volatile = is_volatile;
|
node->data.pointer_type.is_volatile = is_volatile;
|
||||||
node->data.pointer_type.op_expr = child_node;
|
node->data.pointer_type.op_expr = child_node;
|
||||||
|
@ -68,6 +68,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||||||
"tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg",
|
"tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"disallow coercion from non-null-terminated pointer to null-terminated pointer",
|
||||||
|
\\extern fn puts(s: [*]null const u8) c_int;
|
||||||
|
\\pub fn main() void {
|
||||||
|
\\ const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'};
|
||||||
|
\\ const no_zero_ptr: [*]const u8 = &no_zero_array;
|
||||||
|
\\ _ = puts(no_zero_ptr);
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
"tmp.zig:5:14: error: expected type '[*]null const u8', found '[*]const u8'",
|
||||||
|
);
|
||||||
|
|
||||||
cases.add(
|
cases.add(
|
||||||
"atomic orderings of atomicStore Acquire or AcqRel",
|
"atomic orderings of atomicStore Acquire or AcqRel",
|
||||||
\\export fn entry() void {
|
\\export fn entry() void {
|
||||||
|
@ -200,3 +200,17 @@ test "assign null directly to C pointer and test null equality" {
|
|||||||
}
|
}
|
||||||
comptime expect((y1 orelse &othery) == y1);
|
comptime expect((y1 orelse &othery) == y1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "null terminated pointer" {
|
||||||
|
const S = struct {
|
||||||
|
fn doTheTest() void {
|
||||||
|
var array_with_zero = [_]u8{'h', 'e', 'l', 'l', 'o', 0};
|
||||||
|
var zero_ptr: [*]null const u8 = @ptrCast([*]null const u8, &array_with_zero);
|
||||||
|
var no_zero_ptr: [*]const u8 = zero_ptr;
|
||||||
|
expect(std.mem.eql(u8, std.mem.toSliceConst(u8, no_zero_ptr), "hello"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
S.doTheTest();
|
||||||
|
// TODO test fails at comptime
|
||||||
|
//comptime S.doTheTest();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user