Commit 7d910b024b

Vexu <git@vexu.eu>
2020-09-09 16:41:51
stage2: very basic imports
1 parent 20ae159
src/Module.zig
@@ -75,6 +75,9 @@ global_error_set: std.StringHashMapUnmanaged(u16) = .{},
 /// previous analysis.
 generation: u32 = 0,
 
+/// Keys are fully qualified paths
+import_table: std.StringHashMapUnmanaged(*Scope.File) = .{},
+
 stage1_flags: packed struct {
     have_winmain: bool = false,
     have_wwinmain: bool = false,
@@ -208,7 +211,7 @@ pub const Decl = struct {
             .container => {
                 const container = @fieldParentPtr(Scope.Container, "base", self.scope);
                 const tree = container.file_scope.contents.tree;
-                // TODO Container should have it's own decls()
+                // TODO Container should have its own decls()
                 const decl_node = tree.root_node.decls()[self.src_index];
                 return tree.token_locs[decl_node.firstToken()].start;
             },
@@ -532,12 +535,12 @@ pub const Scope = struct {
 
         /// Direct children of the file.
         decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
-
-        // TODO implement container types and put this in a status union
-        // ty: Type
+        ty: Type,
 
         pub fn deinit(self: *Container, gpa: *Allocator) void {
             self.decls.deinit(gpa);
+            // TODO either Container of File should have an arena for sub_file_path and ty
+            gpa.destroy(self.ty.cast(Type.Payload.EmptyStruct).?);
             self.* = undefined;
         }
 
@@ -2381,9 +2384,41 @@ pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst,
     return self.fail(scope, src, "TODO implement analysis of slice", .{});
 }
 
-pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) InnerError!*Inst {
-    // TODO actually try to import
-    return self.constType(scope, src, Type.initTag(.empty_struct));
+pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) !*Scope.File {
+    // TODO if (package_table.get(target_string)) |pkg|
+
+    const file_path = try std.fs.path.join(scope.arena(), &[_][]const u8{ self.root_pkg.root_src_dir_path, target_string });
+
+    if (self.import_table.get(file_path)) |some| {
+        return some;
+    }
+
+    // TODO check for imports outside of pkg path
+    if (false) return error.ImportOutsidePkgPath;
+
+    // TODO Scope.Container arena for ty and sub_file_path
+    const struct_payload = try self.gpa.create(Type.Payload.EmptyStruct);
+    const file_scope = try self.gpa.create(Scope.File);
+    struct_payload.* = .{ .scope = &file_scope.root_container };
+    file_scope.* = .{
+        .sub_file_path = try self.gpa.dupe(u8, file_path),
+        .source = .{ .unloaded = {} },
+        .contents = .{ .not_available = {} },
+        .status = .never_loaded,
+        .root_container = .{
+            .file_scope = file_scope,
+            .decls = .{},
+            .ty = Type.initPayload(&struct_payload.base),
+        },
+    };
+    self.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
+        error.AnalysisFail => {
+            assert(self.totalErrorCount() != 0);
+        },
+        else => |e| return e,
+    };
+    try self.import_table.put(self.gpa, file_scope.sub_file_path, file_scope);
+    return file_scope;
 }
 
 /// Asserts that lhs and rhs types are both numeric.
src/type.zig
@@ -354,7 +354,6 @@ pub const Type = extern union {
             .enum_literal,
             .anyerror_void_error_union,
             .@"anyframe",
-            .empty_struct,
             => unreachable,
 
             .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
@@ -442,6 +441,7 @@ pub const Type = extern union {
             },
             .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
             .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
+            .empty_struct  => return self.copyPayloadShallow(allocator, Payload.EmptyStruct),
         }
     }
 
@@ -508,6 +508,7 @@ pub const Type = extern union {
                 .@"null" => return out_stream.writeAll("@Type(.Null)"),
                 .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
 
+                // TODO this should print the structs name
                 .empty_struct => return out_stream.writeAll("struct {}"),
                 .@"anyframe" => return out_stream.writeAll("anyframe"),
                 .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
@@ -2837,7 +2838,6 @@ pub const Type = extern union {
         single_const_pointer_to_comptime_int,
         anyerror_void_error_union,
         @"anyframe",
-        empty_struct,
         const_slice_u8, // See last_no_payload_tag below.
         // After this, the tag requires a payload.
 
@@ -2864,6 +2864,7 @@ pub const Type = extern union {
         anyframe_T,
         error_set,
         error_set_single,
+        empty_struct,
 
         pub const last_no_payload_tag = Tag.const_slice_u8;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -2971,6 +2972,14 @@ pub const Type = extern union {
             /// memory is owned by `Module`
             name: []const u8,
         };
+
+        /// Mostly used for namespace like structs with zero fields.
+        /// Most commonly used for files.
+        pub const EmptyStruct = struct {
+            base: Payload = .{ .tag = .empty_struct },
+
+            scope: *Module.Scope.Container,
+        };
     };
 };
 
src/value.zig
@@ -314,6 +314,7 @@ pub const Value = extern union {
             .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
             .anyframe_type => return out_stream.writeAll("anyframe"),
 
+            // TODO this should print `NAME{}`
             .empty_struct_value => return out_stream.writeAll("struct {}{}"),
             .null_value => return out_stream.writeAll("null"),
             .undef => return out_stream.writeAll("undefined"),
src/zir_sema.zig
@@ -1194,7 +1194,19 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
 fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
     const operand = try resolveConstString(mod, scope, inst.positionals.operand);
 
-    return mod.analyzeImport(scope, inst.base.src, operand);
+    const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) {
+        // error.ImportOutsidePkgPath => {
+        //     return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand});
+        // },
+        error.FileNotFound => {
+            return mod.fail(scope, inst.base.src, "unable to find '{}'", .{operand});
+        },
+        else => {
+            // TODO user friendly error to string
+            return mod.fail(scope, inst.base.src, "unable to open '{}': {}", .{operand, @errorName(err)});
+        }
+    };
+    return mod.constType(scope, inst.base.src, file_scope.root_container.ty);
 }
 
 fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {