diff --git a/src/canonicalize/Can.zig b/src/canonicalize/Can.zig index 492cebbc80..4b6f267fd5 100644 --- a/src/canonicalize/Can.zig +++ b/src/canonicalize/Can.zig @@ -305,27 +305,17 @@ pub fn setupAutoImportedBuiltinTypes( if (envs_map.get(type_ident)) |type_entry| { const module_env = type_entry.env; - // Create an import for the parent Builtin module (only once, shared across all types) + // Create an import entry for the parent Builtin module + // This is needed for type bindings to reference, but we do NOT add it to + // import_indices or scope - "Builtin" should not be directly importable! const builtin_module_name = module_env.module_name; - // Check if we already have this import in our indices - const is_new_import = !self.import_indices.contains(builtin_module_name); - const module_import_idx = try self.env.imports.getOrPut( gpa, self.env.common.getStringStore(), builtin_module_name, ); - if (is_new_import) { - // Add to import_indices so getOrCreateAutoImport can find it - try self.import_indices.put(gpa, builtin_module_name, module_import_idx); - - // Also add to current scope so scopeLookupImportedModule can find it - // This ensures consistency with getOrCreateAutoImport - _ = try current_scope.introduceImportedModule(gpa, builtin_module_name, module_import_idx); - } - // Get target_node_idx from statement_idx const target_node_idx = if (type_entry.statement_idx) |stmt_idx| module_env.getExposedNodeIndexByStatementIdx(stmt_idx) diff --git a/src/check/mod.zig b/src/check/mod.zig index b31fd3f9b9..6f62c93860 100644 --- a/src/check/mod.zig +++ b/src/check/mod.zig @@ -40,4 +40,5 @@ test "check tests" { std.testing.refAllDecls(@import("test/let_polymorphism_integration_test.zig")); std.testing.refAllDecls(@import("test/num_type_inference_test.zig")); std.testing.refAllDecls(@import("test/num_type_requirements_test.zig")); + std.testing.refAllDecls(@import("test/builtin_scope_test.zig")); } diff --git a/src/check/test/TestEnv.zig b/src/check/test/TestEnv.zig index ea72a18866..555419b2f1 100644 --- a/src/check/test/TestEnv.zig +++ b/src/check/test/TestEnv.zig @@ -216,21 +216,21 @@ pub fn initWithImport(module_name: []const u8, source: []const u8, other_module_ .builtin_module = other_test_env.builtin_module.env, }; - // Build imported_envs array dynamically based on module_env.imports order - // This matches the production approach in compile_package.zig - const import_count = module_env.imports.imports.items.items.len; - var imported_envs = try std.ArrayList(*const ModuleEnv).initCapacity(gpa, import_count); + // Build imported_envs array + // Always include the builtin module for auto-imported types (Bool, Str, etc.) + var imported_envs = try std.ArrayList(*const ModuleEnv).initCapacity(gpa, 2); defer imported_envs.deinit(gpa); + // Add builtin module unconditionally (needed for auto-imported types) + try imported_envs.append(gpa, other_test_env.builtin_module.env); + + // Process explicit imports + const import_count = module_env.imports.imports.items.items.len; for (module_env.imports.imports.items.items[0..import_count]) |str_idx| { const import_name = module_env.getString(str_idx); - if (std.mem.eql(u8, import_name, "Builtin")) { - try imported_envs.append(gpa, other_test_env.builtin_module.env); - } else if (std.mem.eql(u8, import_name, other_module_name)) { + if (std.mem.eql(u8, import_name, other_module_name)) { // Cross-module import - append the other test module's env try imported_envs.append(gpa, other_test_env.module_env); - } else { - std.debug.print("WARNING: Unknown import in test: {s}\n", .{import_name}); } } @@ -332,22 +332,14 @@ pub fn init(module_name: []const u8, source: []const u8) !TestEnv { .builtin_module = builtin_module.env, }; - // Build imported_envs array dynamically based on module_env.imports order - // This matches the production approach in compile_package.zig - const import_count = module_env.imports.imports.items.items.len; - var imported_envs = try std.ArrayList(*const ModuleEnv).initCapacity(gpa, import_count); + // Build imported_envs array + // Always include the builtin module for auto-imported types (Bool, Str, etc.) + var imported_envs = try std.ArrayList(*const ModuleEnv).initCapacity(gpa, 2); defer imported_envs.deinit(gpa); - for (module_env.imports.imports.items.items[0..import_count]) |str_idx| { - const import_name = module_env.getString(str_idx); - // For tests, all imports are to the Builtin module - if (std.mem.eql(u8, import_name, "Builtin")) { - try imported_envs.append(gpa, builtin_module.env); - } else { - // If there are other imports in the future, handle them here - std.debug.print("WARNING: Unknown import in test: {s}\n", .{import_name}); - } - } + // Add builtin module unconditionally (needed for auto-imported types) + try imported_envs.append(gpa, builtin_module.env); + // Type Check - Pass the imported modules in other_modules parameter var checker = try Check.init( diff --git a/src/check/test/builtin_scope_test.zig b/src/check/test/builtin_scope_test.zig new file mode 100644 index 0000000000..830bae73f3 --- /dev/null +++ b/src/check/test/builtin_scope_test.zig @@ -0,0 +1,60 @@ +//! Tests verifying that "Builtin" is not in scope and cannot be imported, +//! but that nested types like Str, List, etc. are available. + +const TestEnv = @import("./TestEnv.zig"); +const testing = @import("std").testing; +const std = @import("std"); + +test "cannot import Builtin module" { + const src = + \\import Builtin + \\ + \\x = 5 + ; + + var test_env = try TestEnv.init("Test", src); + defer test_env.deinit(); + + // Should have a canonicalization problem because Builtin is not a module that can be imported + const diagnostics = try test_env.module_env.getDiagnostics(); + defer test_env.module_env.gpa.free(diagnostics); + + // Expect at least one diagnostic (module not found error) + try testing.expect(diagnostics.len > 0); +} + +test "can define userspace type named Builtin" { + const src = + \\Test := [A, B, C] + \\ + \\Builtin := [D, E, F] + \\ + \\x : Builtin + \\x = D + ; + + var test_env = try TestEnv.init("Test", src); + defer test_env.deinit(); + + // Should have no problems - Builtin is a valid userspace name + try test_env.assertDefType("x", "Builtin"); +} + +test "builtin types are still available without import" { + const src = + \\Test := [Whatever] + \\ + \\x : Str + \\x = "hello" + \\ + \\y : List(U64) + \\y = [1, 2, 3] + ; + + var test_env = try TestEnv.init("Test", src); + defer test_env.deinit(); + + // Builtin types like Str and List should work without importing Builtin + try test_env.assertDefType("x", "Str"); + try test_env.assertDefType("y", "List(Num(Int(Unsigned64)))"); +}