Commit f7e9d7aa5d

Andrew Kelley <superjoe30@gmail.com>
2017-04-07 21:34:13
ability to implicitly cast integer literal to &const Int
where Int is an integer type also introduce `@intToPtr` builtin for converting a usize to a pointer. users now have to use this instead of `(&T)(int)`. closes #311
1 parent ffb4852
doc/langref.md
@@ -621,3 +621,7 @@ if there is not one specified, invokes the one provided in
 ### @ptrcast(comptime DestType: type, value: var) -> DestType
 
 Converts a pointer of one type to a pointer of another type.
+
+### @intToPtr(comptime DestType: type, int: usize) -> DestType
+
+Converts an integer to a pointer. To convert the other way, use `usize(ptr)`.
src/all_types.hpp
@@ -1199,6 +1199,7 @@ enum BuiltinFnId {
     BuiltinFnIdSetGlobalLinkage,
     BuiltinFnIdPanic,
     BuiltinFnIdPtrCast,
+    BuiltinFnIdIntToPtr,
 };
 
 struct BuiltinFnEntry {
@@ -2391,6 +2392,7 @@ struct IrInstructionPtrToInt {
 struct IrInstructionIntToPtr {
     IrInstruction base;
 
+    IrInstruction *dest_type;
     IrInstruction *target;
 };
 
src/codegen.cpp
@@ -4326,6 +4326,7 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);
     create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);
     create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrcast", 2);
+    create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2);
 }
 
 static void add_compile_var(CodeGen *g, const char *name, ConstExprValue *value) {
src/ir.cpp
@@ -1951,12 +1951,14 @@ static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, As
 }
 
 static IrInstruction *ir_build_int_to_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
-        IrInstruction *target)
+        IrInstruction *dest_type, IrInstruction *target)
 {
     IrInstructionIntToPtr *instruction = ir_build_instruction<IrInstructionIntToPtr>(
             irb, scope, source_node);
+    instruction->dest_type = dest_type;
     instruction->target = target;
 
+    if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
     ir_ref_instruction(target, irb->current_basic_block);
 
     return &instruction->base;
@@ -2185,8 +2187,8 @@ static IrInstruction *ir_instruction_binop_get_dep(IrInstructionBinOp *instructi
 
 static IrInstruction *ir_instruction_declvar_get_dep(IrInstructionDeclVar *instruction, size_t index) {
     switch (index) {
-        case 0: return instruction->var_type;
-        case 1: return instruction->init_value;
+        case 0: return instruction->init_value;
+        case 1: return instruction->var_type;
         default: return nullptr;
     }
 }
@@ -2670,8 +2672,8 @@ static IrInstruction *ir_instruction_ptrcast_get_dep(IrInstructionPtrCast *instr
         size_t index)
 {
     switch (index) {
-        case 0: return instruction->dest_type;
-        case 1: return instruction->ptr;
+        case 0: return instruction->ptr;
+        case 1: return instruction->dest_type;
         default: return nullptr;
     }
 }
@@ -2686,6 +2688,7 @@ static IrInstruction *ir_instruction_widenorshorten_get_dep(IrInstructionWidenOr
 static IrInstruction *ir_instruction_inttoptr_get_dep(IrInstructionIntToPtr *instruction, size_t index) {
     switch (index) {
         case 0: return instruction->target;
+        case 1: return instruction->dest_type;
         default: return nullptr;
     }
 }
@@ -4222,6 +4225,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
 
                 return ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value);
             }
+        case BuiltinFnIdIntToPtr:
+            {
+                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_int_to_ptr(irb, scope, node, arg0_value, arg1_value);
+            }
     }
     zig_unreachable();
 }
@@ -5821,10 +5838,19 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
     }
 
     // implicit number literal to typed number
-    if ((actual_type->id == TypeTableEntryIdNumLitFloat ||
-         actual_type->id == TypeTableEntryIdNumLitInt))
+    // implicit number literal to &const integer
+    if (actual_type->id == TypeTableEntryIdNumLitFloat ||
+         actual_type->id == TypeTableEntryIdNumLitInt)
     {
-        if (ir_num_lit_fits_in_other_type(ira, value, expected_type)) {
+        if (expected_type->id == TypeTableEntryIdPointer &&
+            expected_type->data.pointer.is_const)
+        {
+            if (ir_num_lit_fits_in_other_type(ira, value, expected_type->data.pointer.child_type)) {
+                return ImplicitCastMatchResultYes;
+            } else {
+                return ImplicitCastMatchResultReportedError;
+            }
+        } else if (ir_num_lit_fits_in_other_type(ira, value, expected_type)) {
             return ImplicitCastMatchResultYes;
         } else {
             return ImplicitCastMatchResultReportedError;
@@ -6651,47 +6677,6 @@ static IrInstruction *ir_analyze_ptr_to_int(IrAnalyze *ira, IrInstruction *sourc
     return result;
 }
 
-static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr,
-        IrInstruction *target, TypeTableEntry *wanted_type)
-{
-    assert(wanted_type->id == TypeTableEntryIdPointer);
-
-    if (instr_is_comptime(target)) {
-        ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
-        if (!val)
-            return ira->codegen->invalid_instruction;
-        IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
-                source_instr->source_node, wanted_type);
-        result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
-        result->value.data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&val->data.x_bignum);
-        return result;
-    }
-
-    IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope,
-            source_instr->source_node, target);
-    result->value.type = wanted_type;
-    return result;
-}
-
-static IrInstruction *ir_analyze_int_lit_to_ptr(IrAnalyze *ira, IrInstruction *source_instr,
-        IrInstruction *target, TypeTableEntry *wanted_type)
-{
-    assert(wanted_type->id == TypeTableEntryIdPointer);
-
-    ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
-    if (!val)
-        return ira->codegen->invalid_instruction;
-
-    TypeTableEntry *usize_type = ira->codegen->builtin_types.entry_usize;
-    if (!ir_num_lit_fits_in_other_type(ira, target, usize_type))
-        return ira->codegen->invalid_instruction;
-
-    IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
-            source_instr->source_node, wanted_type);
-    result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
-    result->value.data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&val->data.x_bignum);
-    return result;
-}
 
 static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
         IrInstruction *target, TypeTableEntry *wanted_type)
@@ -6815,7 +6800,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     TypeTableEntry *wanted_type_canon = get_underlying_type(wanted_type);
     TypeTableEntry *actual_type_canon = get_underlying_type(actual_type);
 
-    TypeTableEntry *isize_type = ira->codegen->builtin_types.entry_isize;
     TypeTableEntry *usize_type = ira->codegen->builtin_types.entry_usize;
 
     if (type_is_invalid(wanted_type_canon) || type_is_invalid(actual_type_canon)) {
@@ -6837,28 +6821,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
         return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false);
     }
 
-    // explicit cast from pointer to isize or usize
-    if ((wanted_type_canon == isize_type || wanted_type_canon == usize_type) &&
-        type_is_codegen_pointer(actual_type_canon))
-    {
+    // explicit cast from pointer to usize
+    if (wanted_type_canon == usize_type && type_is_codegen_pointer(actual_type_canon)) {
         return ir_analyze_ptr_to_int(ira, source_instr, value, wanted_type);
     }
 
-
-    // explicit cast from isize or usize to pointer
-    if (wanted_type_canon->id == TypeTableEntryIdPointer &&
-        (actual_type_canon == isize_type || actual_type_canon == usize_type))
-    {
-        return ir_analyze_int_to_ptr(ira, source_instr, value, wanted_type);
-    }
-
-    // explicit cast from number literal to pointer
-    if (wanted_type_canon->id == TypeTableEntryIdPointer &&
-        (actual_type_canon->id == TypeTableEntryIdNumLitInt))
-    {
-        return ir_analyze_int_lit_to_ptr(ira, source_instr, value, wanted_type);
-    }
-
     // explicit widening or shortening cast
     if ((wanted_type_canon->id == TypeTableEntryIdInt &&
         actual_type_canon->id == TypeTableEntryIdInt) ||
@@ -6970,10 +6937,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
     }
 
     // explicit cast from number literal to another type
+    // explicit cast from number literal to &const integer
     if (actual_type->id == TypeTableEntryIdNumLitFloat ||
         actual_type->id == TypeTableEntryIdNumLitInt)
     {
-        if (ir_num_lit_fits_in_other_type(ira, value, wanted_type_canon)) {
+        if (wanted_type->id == TypeTableEntryIdPointer &&
+            wanted_type->data.pointer.is_const)
+        {
+            IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
+            if (type_is_invalid(cast1->value.type))
+                return ira->codegen->invalid_instruction;
+
+            IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
+            if (type_is_invalid(cast2->value.type))
+                return ira->codegen->invalid_instruction;
+
+            return cast2;
+        } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type_canon)) {
             CastOp op;
             if ((actual_type->id == TypeTableEntryIdNumLitFloat &&
                  wanted_type_canon->id == TypeTableEntryIdFloat) ||
@@ -12304,12 +12284,6 @@ static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructio
     return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
 }
 
-static bool is_ptr_type(TypeTableEntry *t) {
-    return (t->id == TypeTableEntryIdPointer || t->id == TypeTableEntryIdFn ||
-            (t->id == TypeTableEntryIdMaybe && (t->data.maybe.child_type->id == TypeTableEntryIdPointer ||
-                t->data.maybe.child_type->id == TypeTableEntryIdFn)));
-}
-
 static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) {
     IrInstruction *dest_type_value = instruction->dest_type->other;
     TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
@@ -12321,12 +12295,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc
     if (type_is_invalid(src_type))
         return ira->codegen->builtin_types.entry_invalid;
 
-    if (!is_ptr_type(src_type)) {
+    if (!type_is_codegen_pointer(src_type)) {
         ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
         return ira->codegen->builtin_types.entry_invalid;
     }
 
-    if (!is_ptr_type(dest_type)) {
+    if (!type_is_codegen_pointer(dest_type)) {
         ir_add_error(ira, dest_type_value,
                 buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
         return ira->codegen->builtin_types.entry_invalid;
@@ -12350,6 +12324,42 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc
     return dest_type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
+    IrInstruction *dest_type_value = instruction->dest_type->other;
+    TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
+    if (type_is_invalid(dest_type))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (!type_is_codegen_pointer(dest_type)) {
+        ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    IrInstruction *target = instruction->target->other;
+    if (type_is_invalid(target->value.type))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    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->builtin_types.entry_invalid;
+
+    if (instr_is_comptime(casted_int)) {
+        ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad);
+        if (!val)
+            return ira->codegen->builtin_types.entry_invalid;
+
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
+        out_val->data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&val->data.x_bignum);
+        return dest_type;
+    }
+
+    IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope,
+            instruction->base.source_node, nullptr, casted_int);
+    ir_link_new_instruction(result, &instruction->base);
+    return dest_type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
         IrInstructionDeclRef *instruction)
 {
@@ -12424,8 +12434,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
     switch (instruction->id) {
         case IrInstructionIdInvalid:
         case IrInstructionIdWidenOrShorten:
-        case IrInstructionIdIntToPtr:
-        case IrInstructionIdPtrToInt:
         case IrInstructionIdIntToEnum:
         case IrInstructionIdIntToErr:
         case IrInstructionIdErrToInt:
@@ -12433,6 +12441,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdEnumFieldPtr:
         case IrInstructionIdInitEnum:
+        case IrInstructionIdPtrToInt:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
@@ -12590,6 +12599,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction);
         case IrInstructionIdPtrCast:
             return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction);
+        case IrInstructionIdIntToPtr:
+            return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction);
         case IrInstructionIdMaybeWrap:
         case IrInstructionIdErrWrapCode:
         case IrInstructionIdErrWrapPayload:
std/debug.zig
@@ -68,7 +68,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
             var maybe_fp: ?&const u8 = @frameAddress();
             while (true) {
                 const fp = maybe_fp ?? break;
-                const return_address = *(&const usize)(usize(fp) + @sizeOf(usize));
+                const return_address = *@intToPtr(&const usize, usize(fp) + @sizeOf(usize));
 
                 const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo;
                 const name = %return compile_unit.die.getAttrString(st, DW.AT_name);
std/hash_map.zig
@@ -234,15 +234,14 @@ test "basicHashMapTest" {
     var map = HashMap(i32, i32, hash_i32, eql_i32).init(&debug.global_allocator);
     defer map.deinit();
 
-    // TODO issue #311
-    assert(%%map.put(1, i32(11)) == null);
-    assert(%%map.put(2, i32(22)) == null);
-    assert(%%map.put(3, i32(33)) == null);
-    assert(%%map.put(4, i32(44)) == null);
-    assert(%%map.put(5, i32(55)) == null);
-
-    assert(??%%map.put(5, i32(66)) == 55);
-    assert(??%%map.put(5, i32(55)) == 66);
+    assert(%%map.put(1, 11) == null);
+    assert(%%map.put(2, 22) == null);
+    assert(%%map.put(3, 33) == null);
+    assert(%%map.put(4, 44) == null);
+    assert(%%map.put(5, 55) == null);
+
+    assert(??%%map.put(5, 66) == 55);
+    assert(??%%map.put(5, 55) == 66);
 
     assert((??map.get(2)).value == 22);
     _ = map.remove(2);
test/cases/cast.zig
@@ -1,15 +1,15 @@
 const assert = @import("std").debug.assert;
 const mem = @import("std").mem;
 
-test "intToPtrCast" {
-    const x = isize(13);
-    const y = (&u8)(x);
+test "int to ptr cast" {
+    const x = usize(13);
+    const y = @intToPtr(&u8, x);
     const z = usize(y);
     assert(z == 13);
 }
 
 test "numLitIntToPtrCast" {
-    const vga_mem = (&u16)(0xB8000);
+    const vga_mem = @intToPtr(&u16, 0xB8000);
     assert(usize(vga_mem) == 0xB8000);
 }
 
@@ -64,3 +64,9 @@ fn testPeerResolveArrayConstSlice(b: bool) {
     assert(mem.eql(u8, value1, "aoeu"));
     assert(mem.eql(u8, value2, "zz"));
 }
+
+
+test "integer literal to &const int" {
+    const x: &const i32 = 3;
+    assert(*x == 3);
+}
test/cases/misc.zig
@@ -373,14 +373,6 @@ fn testTakeAddressOfParameter(f: f32) {
 }
 
 
-test "intToPtrCast" {
-    const x = isize(13);
-    const y = (&u8)(x);
-    const z = usize(y);
-    assert(z == 13);
-}
-
-
 test "pointerComparison" {
     const a = ([]const u8)("a");
     const b = &a;