added C pointer type and implicit int-to-ptr for this type

See #1059
This commit is contained in:
Andrew Kelley 2019-02-10 00:14:30 -05:00
parent caf672c495
commit b8cbe3872e
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
7 changed files with 223 additions and 88 deletions

View File

@ -1038,6 +1038,7 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
enum PtrLen {
PtrLenUnknown,
PtrLenSingle,
PtrLenC,
};
struct ZigTypePointer {

View File

@ -417,6 +417,18 @@ ZigType *get_promise_type(CodeGen *g, ZigType *result_type) {
return entry;
}
static const char *ptr_len_to_star_str(PtrLen ptr_len) {
switch (ptr_len) {
case PtrLenSingle:
return "*";
case PtrLenUnknown:
return "[*]";
case PtrLenC:
return "[*c]";
}
zig_unreachable();
}
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
@ -466,7 +478,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]";
const char *star_str = ptr_len_to_star_str(ptr_len);
const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
buf_resize(&entry->name, 0);

View File

@ -169,6 +169,10 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type);
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *dest_type);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
@ -5019,10 +5023,23 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *
return ir_build_ref(irb, scope, value->source_node, value, false, false);
}
static PtrLen star_token_to_ptr_len(TokenId token_id) {
switch (token_id) {
case TokenIdStar:
case TokenIdStarStar:
return PtrLenSingle;
case TokenIdBracketStarBracket:
return PtrLenUnknown;
case TokenIdBracketStarCBracket:
return PtrLenC;
default:
zig_unreachable();
}
}
static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypePointerType);
PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
bool is_const = node->data.pointer_type.is_const;
bool is_volatile = node->data.pointer_type.is_volatile;
AstNode *expr_node = node->data.pointer_type.op_expr;
@ -8538,6 +8555,20 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
}
}
}
if (other_type->id == ZigTypeIdPointer && other_type->data.pointer.ptr_len == PtrLenC && const_val_is_int) {
if (!bigint_fits_in_bits(&const_val->data.x_bigint, ira->codegen->pointer_size_bytes * 8, true) &&
!bigint_fits_in_bits(&const_val->data.x_bigint, ira->codegen->pointer_size_bytes * 8, false))
{
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
ir_add_error(ira, instruction,
buf_sprintf("integer value %s outside of pointer address range",
buf_ptr(val_buf)));
return false;
}
return true;
}
const char *num_lit_str;
Buf *val_buf = buf_alloc();
@ -10811,6 +10842,37 @@ static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *
return ir_build_vector_to_array(ira, source_instr, vector, array_type);
}
static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *integer, ZigType *dest_type)
{
IrInstruction *unsigned_integer;
if (instr_is_comptime(integer)) {
unsigned_integer = integer;
} else {
assert(integer->value.type->id == ZigTypeIdInt);
if (integer->value.type->data.integral.bit_count >
ira->codegen->builtin_types.entry_usize->data.integral.bit_count)
{
ir_add_error(ira, source_instr,
buf_sprintf("integer type too big for implicit @intToPtr to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if (integer->value.type->data.integral.is_signed) {
ZigType *unsigned_int_type = get_int_type(ira->codegen, false,
integer->value.type->data.integral.bit_count);
unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type);
if (type_is_invalid(unsigned_integer->value.type))
return ira->codegen->invalid_instruction;
} else {
unsigned_integer = integer;
}
}
return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type);
}
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
@ -11217,6 +11279,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
}
// casting to C pointers
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC) {
// cast from integer to C pointer
if (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt) {
return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type);
}
}
// cast from undefined to anything
if (actual_type->id == ZigTypeIdUndefined) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
@ -20674,17 +20744,38 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
zig_unreachable();
}
static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
Error err;
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
static bool type_can_bit_cast(ZigType *t) {
switch (t->id) {
case ZigTypeIdInvalid:
zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdNamespace:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdPointer:
return false;
default:
// TODO list these types out explicitly, there are probably some other invalid ones here
return true;
}
}
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
ZigType *dest_type)
{
Error err;
IrInstruction *value = instruction->value->child;
ZigType *src_type = value->value.type;
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
assert(get_codegen_ptr_type(src_type) == nullptr);
assert(type_can_bit_cast(src_type));
assert(get_codegen_ptr_type(dest_type) == nullptr);
assert(type_can_bit_cast(dest_type));
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
@ -20692,60 +20783,11 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
if (get_codegen_ptr_type(src_type) != nullptr) {
ir_add_error(ira, value,
buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
switch (src_type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdNamespace:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
default:
break;
}
if (get_codegen_ptr_type(dest_type) != nullptr) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
switch (dest_type->id) {
case ZigTypeIdInvalid:
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdNamespace:
case ZigTypeIdUnreachable:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
default:
break;
}
uint64_t dest_size_bytes = type_size(ira->codegen, dest_type);
uint64_t src_size_bytes = type_size(ira->codegen, src_type);
if (dest_size_bytes != src_size_bytes) {
ir_add_error(ira, &instruction->base,
ir_add_error(ira, source_instr,
buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64,
buf_ptr(&dest_type->name), dest_size_bytes,
buf_ptr(&src_type->name), src_size_bytes));
@ -20755,7 +20797,7 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type);
uint64_t src_size_bits = type_size_bits(ira->codegen, src_type);
if (dest_size_bits != src_size_bits) {
ir_add_error(ira, &instruction->base,
ir_add_error(ira, source_instr,
buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits",
buf_ptr(&dest_type->name), dest_size_bits,
buf_ptr(&src_type->name), src_size_bits));
@ -20767,20 +20809,86 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
IrInstruction *result = ir_const(ira, source_instr, dest_type);
uint8_t *buf = allocate_nonzero<uint8_t>(src_size_bytes);
buf_write_value_bytes(ira->codegen, buf, val);
if ((err = buf_read_value_bytes(ira, ira->codegen, instruction->base.source_node, buf, &result->value)))
if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value)))
return ira->codegen->invalid_instruction;
return result;
}
IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, value);
IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope,
source_instr->source_node, nullptr, value);
result->value.type = dest_type;
return result;
}
static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
IrInstruction *value = instruction->value->child;
ZigType *src_type = value->value.type;
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
if (get_codegen_ptr_type(src_type) != nullptr) {
ir_add_error(ira, value,
buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
if (!type_can_bit_cast(src_type)) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
if (get_codegen_ptr_type(dest_type) != nullptr) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
if (!type_can_bit_cast(dest_type)) {
ir_add_error(ira, dest_type_value,
buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type);
}
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
ZigType *ptr_type)
{
assert(get_src_ptr_type(ptr_type) != nullptr);
assert(type_has_bits(ptr_type));
IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize);
if (type_is_invalid(casted_int->value.type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_int)) {
ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, source_instr, ptr_type);
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint);
return result;
}
IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope,
source_instr->source_node, nullptr, casted_int);
result->value.type = ptr_type;
return result;
}
static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
Error err;
IrInstruction *dest_type_value = instruction->dest_type->child;
@ -20802,30 +20910,12 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru
return ira->codegen->invalid_instruction;
}
IrInstruction *target = instruction->target->child;
if (type_is_invalid(target->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize);
if (type_is_invalid(casted_int->value.type))
return ira->codegen->invalid_instruction;
if (instr_is_comptime(casted_int)) {
ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint);
return result;
}
IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, nullptr, casted_int);
result->value.type = dest_type;
return result;
return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type);
}
static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira,

View File

@ -2779,6 +2779,7 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) {
// <- ASTERISK
// / ASTERISK2
// / LBRACKET ASTERISK RBRACKET
// / LBRACKET ASTERISK C RBRACKET
static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
Token *asterisk = eat_token_if(pc, TokenIdStar);
if (asterisk != nullptr) {
@ -2804,6 +2805,13 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
return res;
}
Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket);
if (cptr != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr);
res->data.pointer_type.star_token = cptr;
return res;
}
return nullptr;
}

View File

@ -221,6 +221,7 @@ enum TokenizeState {
TokenizeStateError,
TokenizeStateLBracket,
TokenizeStateLBracketStar,
TokenizeStateLBracketStarC,
};
@ -846,7 +847,6 @@ void tokenize(Buf *buf, Tokenization *out) {
switch (c) {
case '*':
t.state = TokenizeStateLBracketStar;
set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
break;
default:
// reinterpret as just an lbracket
@ -857,6 +857,21 @@ void tokenize(Buf *buf, Tokenization *out) {
}
break;
case TokenizeStateLBracketStar:
switch (c) {
case 'c':
t.state = TokenizeStateLBracketStarC;
set_token_id(&t, t.cur_tok, TokenIdBracketStarCBracket);
break;
case ']':
set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
end_token(&t);
t.state = TokenizeStateStart;
break;
default:
invalid_char_error(&t, c);
}
break;
case TokenizeStateLBracketStarC:
switch (c) {
case ']':
end_token(&t);
@ -1491,6 +1506,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateLineStringContinue:
case TokenizeStateLineStringContinueC:
case TokenizeStateLBracketStar:
case TokenizeStateLBracketStarC:
tokenize_error(&t, "unexpected EOF");
break;
case TokenizeStateLineComment:
@ -1528,6 +1544,7 @@ const char * token_name(TokenId id) {
case TokenIdBitShiftRightEq: return ">>=";
case TokenIdBitXorEq: return "^=";
case TokenIdBracketStarBracket: return "[*]";
case TokenIdBracketStarCBracket: return "[*c]";
case TokenIdCharLiteral: return "CharLiteral";
case TokenIdCmpEq: return "==";
case TokenIdCmpGreaterOrEq: return ">=";

View File

@ -29,6 +29,7 @@ enum TokenId {
TokenIdBitShiftRightEq,
TokenIdBitXorEq,
TokenIdBracketStarBracket,
TokenIdBracketStarCBracket,
TokenIdCharLiteral,
TokenIdCmpEq,
TokenIdCmpGreaterOrEq,

View File

@ -42,3 +42,9 @@ test "double pointer parsing" {
fn PtrOf(comptime T: type) type {
return *T;
}
test "assigning integer to C pointer" {
var x: i32 = 0;
var ptr: [*c]u8 = 0;
var ptr2: [*c]u8 = x;
}