Commit f25182f46d

Andrew Kelley <andrew@ziglang.org>
2019-11-23 23:51:37
structs can have fields with type `var`
behavior tests passing now
1 parent 7597735
lib/std/builtin.zig
@@ -146,10 +146,8 @@ pub const TypeInfo = union(enum) {
         is_allowzero: bool,
         /// The type of the sentinel is the element type of the pointer, which is
         /// the value of the `child` field in this struct. However there is no way
-        /// to refer to that type here, so this is a pointer to an opaque value.
-        /// It will be known at compile-time to be the correct type. Dereferencing
-        /// this pointer will work at compile-time.
-        sentinel: ?*const c_void,
+        /// to refer to that type here, so we use `var`.
+        sentinel: var,
 
         /// This data structure is used by the Zig language code generation and
         /// therefore must be kept in sync with the compiler implementation.
@@ -168,10 +166,8 @@ pub const TypeInfo = union(enum) {
         child: type,
         /// The type of the sentinel is the element type of the array, which is
         /// the value of the `child` field in this struct. However there is no way
-        /// to refer to that type here, so this is a pointer to an opaque value.
-        /// It will be known at compile-time to be the correct type. Dereferencing
-        /// this pointer will work at compile-time.
-        sentinel: ?*const c_void,
+        /// to refer to that type here, so we use `var`.
+        sentinel: var,
     };
 
     /// This data structure is used by the Zig language code generation and
src/all_types.hpp
@@ -601,6 +601,7 @@ enum NodeType {
     NodeTypeSuspend,
     NodeTypeAnyFrameType,
     NodeTypeEnumLiteral,
+    NodeTypeVarFieldType,
 };
 
 enum CallingConvention {
src/analyze.cpp
@@ -1179,6 +1179,10 @@ Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *
 Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type) {
     if (type_val->special != ConstValSpecialLazy) {
         assert(type_val->special == ConstValSpecialStatic);
+        if (type_val->data.x_type == g->builtin_types.entry_var) {
+            *is_opaque_type = false;
+            return ErrorNone;
+        }
         *is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque);
         return ErrorNone;
     }
@@ -3667,6 +3671,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeEnumLiteral:
         case NodeTypeAnyFrameType:
         case NodeTypeErrorSetField:
+        case NodeTypeVarFieldType:
             zig_unreachable();
     }
 }
@@ -5587,6 +5592,9 @@ ConstExprValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
 
 ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
     Error err;
+    if (ty == g->builtin_types.entry_var) {
+        return ReqCompTimeYes;
+    }
     switch (ty->id) {
         case ZigTypeIdInvalid:
             zig_unreachable();
src/ast_render.cpp
@@ -268,6 +268,8 @@ static const char *node_type_str(NodeType node_type) {
             return "EnumLiteral";
         case NodeTypeErrorSetField:
             return "ErrorSetField";
+        case NodeTypeVarFieldType:
+            return "VarFieldType";
     }
     zig_unreachable();
 }
@@ -1184,6 +1186,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
                 break;
             }
+        case NodeTypeVarFieldType: {
+            fprintf(ar->f, "var");
+            break;
+        }
         case NodeTypeParamDecl:
         case NodeTypeTestDecl:
         case NodeTypeStructField:
src/ir.cpp
@@ -8556,6 +8556,9 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             add_node_error(irb->codegen, node,
                 buf_sprintf("inferred array size invalid here"));
             return irb->codegen->invalid_instruction;
+        case NodeTypeVarFieldType:
+            return ir_lval_wrap(irb, scope,
+                    ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc);
     }
     zig_unreachable();
 }
@@ -8715,6 +8718,9 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal
     assert(val != nullptr);
     assert(const_val->type->id == ZigTypeIdPointer);
     ZigType *expected_type = const_val->type->data.pointer.child_type;
+    if (expected_type == codegen->builtin_types.entry_var) {
+        return val;
+    }
     switch (type_has_one_possible_value(codegen, expected_type)) {
         case OnePossibleValueInvalid:
             return nullptr;
@@ -13502,6 +13508,9 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
         }
         if (ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
             ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
+            if (child_type == ira->codegen->builtin_types.entry_var) {
+                child_type = pointee->type;
+            }
             if (pointee->special != ConstValSpecialRuntime) {
                 IrInstruction *result = ir_const(ira, source_instruction, child_type);
 
@@ -19857,13 +19866,24 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns
     return ir_analyze_test_non_null(ira, &instruction->base, value);
 }
 
+static ZigType *get_ptr_elem_type(CodeGen *g, IrInstruction *ptr) {
+    ir_assert(ptr->value.type->id == ZigTypeIdPointer, ptr);
+    ZigType *elem_type = ptr->value.type->data.pointer.child_type;
+    if (elem_type != g->builtin_types.entry_var)
+        return elem_type;
+
+    if (ir_resolve_lazy(g, ptr->source_node, &ptr->value))
+        return g->builtin_types.entry_invalid;
+
+    assert(value_is_comptime(&ptr->value));
+    ConstExprValue *pointee = const_ptr_pointee_unchecked(g, &ptr->value);
+    return pointee->type;
+}
+
 static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr,
         IrInstruction *base_ptr, bool safety_check_on, bool initializing)
 {
-    ZigType *ptr_type = base_ptr->value.type;
-    assert(ptr_type->id == ZigTypeIdPointer);
-
-    ZigType *type_entry = ptr_type->data.pointer.child_type;
+    ZigType *type_entry = get_ptr_elem_type(ira->codegen, base_ptr);
     if (type_is_invalid(type_entry))
         return ira->codegen->invalid_instruction;
 
@@ -19901,7 +19921,8 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr
 
     ZigType *child_type = type_entry->data.maybe.child_type;
     ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
-            ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false);
+            base_ptr->value.type->data.pointer.is_const, base_ptr->value.type->data.pointer.is_volatile,
+            PtrLenSingle, 0, 0, 0, false);
 
     bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry);
 
@@ -21627,20 +21648,15 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
     fields[5]->special = ConstValSpecialStatic;
     fields[5]->type = ira->codegen->builtin_types.entry_bool;
     fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
-    // sentinel: ?*const c_void
-    ZigType *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_c_void, true);
+    // sentinel: var
     ensure_field_index(result->type, "sentinel", 6);
     fields[6]->special = ConstValSpecialStatic;
-    fields[6]->type = get_optional_type(ira->codegen, ptr_type);
-    if (attrs_type->data.pointer.sentinel == nullptr) {
-        fields[6]->data.x_optional = nullptr;
+    if (attrs_type->data.pointer.sentinel != nullptr) {
+        fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
+        fields[6]->data.x_optional = attrs_type->data.pointer.sentinel;
     } else {
-        ConstExprValue *ptr_val = create_const_vals(1);
-        fields[6]->data.x_optional = ptr_val;
-        ptr_val->data.x_ptr.special = ConstPtrSpecialRef;
-        ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst;
-        ptr_val->data.x_ptr.data.ref.pointee = create_const_vals(1);
-        copy_const_val(ptr_val->data.x_ptr.data.ref.pointee, attrs_type->data.pointer.sentinel, false);
+        fields[6]->type = ira->codegen->builtin_types.entry_null;
+        fields[6]->data.x_optional = nullptr;
     }
 
     return result;
@@ -21762,23 +21778,10 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
                 fields[1]->special = ConstValSpecialStatic;
                 fields[1]->type = ira->codegen->builtin_types.entry_type;
                 fields[1]->data.x_type = type_entry->data.array.child_type;
-                // sentinel: ?*const c_void
+                // sentinel: var
                 fields[2]->special = ConstValSpecialStatic;
-                ZigType *ptr_type = get_pointer_to_type(ira->codegen,
-                        ira->codegen->builtin_types.entry_c_void, true);
-                fields[2]->type = get_optional_type(ira->codegen, ptr_type);
-                if (type_entry->data.array.sentinel == nullptr) {
-                    fields[2]->data.x_optional = nullptr;
-                } else {
-                    ConstExprValue *ptr_val = create_const_vals(1);
-                    fields[2]->data.x_optional = ptr_val;
-                    ptr_val->type = ptr_type;
-                    ptr_val->data.x_ptr.special = ConstPtrSpecialRef;
-                    ptr_val->data.x_ptr.mut = ConstPtrMutComptimeConst;
-                    ptr_val->data.x_ptr.data.ref.pointee = create_const_vals(1);
-                    copy_const_val(ptr_val->data.x_ptr.data.ref.pointee, type_entry->data.array.sentinel, false);
-                }
-
+                fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type);
+                fields[2]->data.x_optional = type_entry->data.array.sentinel;
                 break;
             }
         case ZigTypeIdVector: {
@@ -22290,15 +22293,18 @@ static ConstExprValue *get_const_field(IrAnalyze *ira, ConstExprValue *struct_va
     return struct_value->data.x_struct.fields[field_index];
 }
 
-static ConstExprValue *get_const_field_variant(IrAnalyze *ira, ConstExprValue *struct_value,
-        const char *name, size_t field_index)
+static Error get_const_field_sentinel(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *struct_value,
+        const char *name, size_t field_index, ZigType *elem_type, ConstExprValue **result)
 {
     ConstExprValue *field_val = get_const_field(ira, struct_value, name, field_index);
-    assert(field_val->type->id == ZigTypeIdOptional);
-    ConstExprValue *opt_val = field_val->data.x_optional;
-    if (opt_val == nullptr) return nullptr;
-    assert(opt_val->type->id == ZigTypeIdPointer);
-    return const_ptr_pointee_unchecked(ira->codegen, opt_val);
+    IrInstruction *field_inst = ir_const(ira, source_instr, field_val->type);
+    IrInstruction *casted_field_inst = ir_implicit_cast(ira, field_inst,
+            get_optional_type(ira->codegen, elem_type));
+    if (type_is_invalid(casted_field_inst->value.type))
+        return ErrorSemanticAnalyzeFail;
+
+    *result = casted_field_inst->value.data.x_optional;
+    return ErrorNone;
 }
 
 static bool get_const_field_bool(IrAnalyze *ira, ConstExprValue *struct_value, const char *name, size_t field_index)
@@ -22323,6 +22329,7 @@ static ZigType *get_const_field_meta_type(IrAnalyze *ira, ConstExprValue *struct
 }
 
 static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, ZigTypeId tagTypeId, ConstExprValue *payload) {
+    Error err;
     switch (tagTypeId) {
         case ZigTypeIdInvalid:
             zig_unreachable();
@@ -22364,8 +22371,16 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi
                 assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type));
                 BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag);
                 PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index);
+                ZigType *elem_type = get_const_field_meta_type(ira, payload, "child", 4);
+                ConstExprValue *sentinel;
+                if ((err = get_const_field_sentinel(ira, instruction, payload, "sentinel", 6,
+                                elem_type, &sentinel)))
+                {
+                    return nullptr;
+                }
+
                 ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen,
-                    get_const_field_meta_type(ira, payload, "child", 4),
+                    elem_type,
                     get_const_field_bool(ira, payload, "is_const", 1),
                     get_const_field_bool(ira, payload, "is_volatile", 2),
                     ptr_len,
@@ -22373,22 +22388,26 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInstruction *instruction, Zi
                     0, // bit_offset_in_host
                     0, // host_int_bytes
                     get_const_field_bool(ira, payload, "is_allowzero", 5),
-                    VECTOR_INDEX_NONE,
-                    nullptr,
-                    get_const_field_variant(ira, payload, "sentinel", 6)
-                );
+                    VECTOR_INDEX_NONE, nullptr, sentinel);
                 if (size_enum_index != 2)
                     return ptr_type;
                 return get_slice_type(ira->codegen, ptr_type);
             }
-        case ZigTypeIdArray:
+        case ZigTypeIdArray: {
             assert(payload->special == ConstValSpecialStatic);
             assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr));
+            ZigType *elem_type = get_const_field_meta_type(ira, payload, "child", 1);
+            ConstExprValue *sentinel;
+            if ((err = get_const_field_sentinel(ira, instruction, payload, "sentinel", 2,
+                            elem_type, &sentinel)))
+            {
+                return nullptr;
+            }
             return get_array_type(ira->codegen,
-                get_const_field_meta_type(ira, payload, "child", 1),
+                elem_type,
                 bigint_as_u64(get_const_field_lit_int(ira, payload, "len", 0)),
-                get_const_field_variant(ira, payload, "sentinel", 2)
-            );
+                sentinel);
+        }
         case ZigTypeIdComptimeFloat:
             return ira->codegen->builtin_types.entry_num_lit_float;
         case ZigTypeIdComptimeInt:
src/parser.cpp
@@ -848,7 +848,12 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
 
     AstNode *type_expr = nullptr;
     if (eat_token_if(pc, TokenIdColon) != nullptr) {
-        type_expr = ast_expect(pc, ast_parse_type_expr);
+        Token *var_tok = eat_token_if(pc, TokenIdKeywordVar);
+        if (var_tok != nullptr) {
+            type_expr = ast_create_node(pc, NodeTypeVarFieldType, var_tok);
+        } else {
+            type_expr = ast_expect(pc, ast_parse_type_expr);
+        }
     }
     AstNode *align_expr = ast_parse_byte_align(pc);
     AstNode *expr = nullptr;
@@ -3163,6 +3168,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
             visit_field(&node->data.suspend.block, visit, context);
             break;
         case NodeTypeEnumLiteral:
+        case NodeTypeVarFieldType:
             break;
     }
 }
test/stage1/behavior/type.zig
@@ -112,7 +112,7 @@ test "Type.Array" {
         .Array = TypeInfo.Array{
             .len = 2,
             .child = u32,
-            .sentinel = &0,
+            .sentinel = 0,
         },
     }));
     testTypes([_]type{ [1]u8, [30]usize, [7]bool });
test/stage1/behavior/type_info.zig
@@ -46,7 +46,7 @@ fn testPointer() void {
     expect(u32_ptr_info.Pointer.is_volatile == false);
     expect(u32_ptr_info.Pointer.alignment == @alignOf(u32));
     expect(u32_ptr_info.Pointer.child == u32);
-    expect(u32_ptr_info.Pointer.is_null_terminated == false);
+    expect(u32_ptr_info.Pointer.sentinel == null);
 }
 
 test "type info: unknown length pointer type info" {
@@ -60,7 +60,7 @@ fn testUnknownLenPtr() void {
     expect(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
     expect(u32_ptr_info.Pointer.is_const == true);
     expect(u32_ptr_info.Pointer.is_volatile == true);
-    expect(u32_ptr_info.Pointer.is_null_terminated == false);
+    expect(u32_ptr_info.Pointer.sentinel == null);
     expect(u32_ptr_info.Pointer.alignment == @alignOf(f64));
     expect(u32_ptr_info.Pointer.child == f64);
 }
@@ -76,7 +76,7 @@ fn testNullTerminatedPtr() void {
     expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
     expect(ptr_info.Pointer.is_const == false);
     expect(ptr_info.Pointer.is_volatile == false);
-    expect(ptr_info.Pointer.is_null_terminated == true);
+    expect(ptr_info.Pointer.sentinel.? == 0);
 
     expect(@typeInfo([:0]u8).Pointer.sentinel != null);
     expect(@typeInfo([10:0]u8).Array.sentinel != null);