aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-08-28 15:35:59 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-09-01 17:54:06 -0700
commit332eafeb7f3d866556ab767b960a04661bc43bc7 (patch)
treed299cbc233ca756ca42885e66f54e2b08f2a3d9a /src/Sema.zig
parentc05a20fc8c36742dab8792d15e79716da1a55759 (diff)
downloadzig-332eafeb7f3d866556ab767b960a04661bc43bc7.tar.gz
zig-332eafeb7f3d866556ab767b960a04661bc43bc7.zip
stage2: first pass at implementing usingnamespace
Ran into a design flaw here which will need to get solved by having AstGen annotate ZIR with which instructions are closed over.
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig115
1 files changed, 99 insertions, 16 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 108470ec79..b3eb3b1b85 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -58,6 +58,9 @@ comptime_args_fn_inst: Zir.Inst.Index = 0,
/// extra hash table lookup in the `monomorphed_funcs` set.
/// Sema will set this to null when it takes ownership.
preallocated_new_func: ?*Module.Fn = null,
+/// Collects struct, union, enum, and opaque decls which need to have their
+/// fields resolved before this Sema is deinitialized.
+types_pending_resolution: std.ArrayListUnmanaged(Type) = .{},
const std = @import("std");
const mem = std.mem;
@@ -90,6 +93,7 @@ pub fn deinit(sema: *Sema) void {
sema.air_values.deinit(gpa);
sema.inst_map.deinit(gpa);
sema.decl_val_table.deinit(gpa);
+ sema.types_pending_resolution.deinit(gpa);
sema.* = undefined;
}
@@ -908,7 +912,9 @@ fn zirStructDecl(
&struct_obj.namespace, new_decl, new_decl.name,
});
try sema.analyzeStructDecl(new_decl, inst, struct_obj);
+ try sema.types_pending_resolution.ensureUnusedCapacity(sema.gpa, 1);
try new_decl.finalizeNewArena(&new_decl_arena);
+ sema.types_pending_resolution.appendAssumeCapacity(struct_ty);
return sema.analyzeDeclVal(block, src, new_decl);
}
@@ -1198,7 +1204,9 @@ fn zirUnionDecl(
_ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
+ try sema.types_pending_resolution.ensureUnusedCapacity(sema.gpa, 1);
try new_decl.finalizeNewArena(&new_decl_arena);
+ sema.types_pending_resolution.appendAssumeCapacity(union_ty);
return sema.analyzeDeclVal(block, src, new_decl);
}
@@ -2324,42 +2332,105 @@ fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
}
fn lookupIdentifier(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, name: []const u8) !*Decl {
- // TODO emit a compile error if more than one decl would be matched.
var namespace = sema.namespace;
while (true) {
- if (try sema.lookupInNamespace(namespace, name)) |decl| {
+ if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl| {
return decl;
}
namespace = namespace.parent orelse break;
}
- return sema.mod.fail(&block.base, src, "use of undeclared identifier '{s}'", .{name});
+ unreachable; // AstGen detects use of undeclared identifier errors.
}
/// This looks up a member of a specific namespace. It is affected by `usingnamespace` but
/// only for ones in the specified namespace.
fn lookupInNamespace(
sema: *Sema,
+ block: *Scope.Block,
+ src: LazySrcLoc,
namespace: *Scope.Namespace,
ident_name: []const u8,
+ observe_usingnamespace: bool,
) CompileError!?*Decl {
+ const mod = sema.mod;
+
const namespace_decl = namespace.getDecl();
if (namespace_decl.analysis == .file_failure) {
- try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);
+ try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
return error.AnalysisFail;
}
- // TODO implement usingnamespace
- if (namespace.decls.get(ident_name)) |decl| {
- try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+ if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) {
+ const src_file = block.src_decl.namespace.file_scope;
+
+ const gpa = sema.gpa;
+ var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Scope.Namespace, void) = .{};
+ defer checked_namespaces.deinit(gpa);
+
+ // Keep track of name conflicts for error notes.
+ var candidates: std.ArrayListUnmanaged(*Decl) = .{};
+ defer candidates.deinit(gpa);
+
+ try checked_namespaces.put(gpa, namespace, {});
+ var check_i: usize = 0;
+
+ while (check_i < checked_namespaces.count()) : (check_i += 1) {
+ const check_ns = checked_namespaces.keys()[check_i];
+ if (check_ns.decls.get(ident_name)) |decl| {
+ // Skip decls which are not marked pub, which are in a different
+ // file than the `a.b`/`@hasDecl` syntax.
+ if (decl.is_pub or src_file == decl.namespace.file_scope) {
+ try candidates.append(gpa, decl);
+ }
+ }
+ var it = check_ns.usingnamespace_set.iterator();
+ while (it.next()) |entry| {
+ const sub_usingnamespace_decl = entry.key_ptr.*;
+ const sub_is_pub = entry.value_ptr.*;
+ if (!sub_is_pub and src_file != sub_usingnamespace_decl.namespace.file_scope) {
+ // Skip usingnamespace decls which are not marked pub, which are in
+ // a different file than the `a.b`/`@hasDecl` syntax.
+ continue;
+ }
+ try sema.ensureDeclAnalyzed(sub_usingnamespace_decl);
+ const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data;
+ const sub_ns = ns_ty.getNamespace().?;
+ try checked_namespaces.put(gpa, sub_ns, {});
+ }
+ }
+
+ switch (candidates.items.len) {
+ 0 => {},
+ 1 => {
+ const decl = candidates.items[0];
+ try mod.declareDeclDependency(sema.owner_decl, decl);
+ return decl;
+ },
+ else => {
+ const msg = msg: {
+ const msg = try mod.errMsg(&block.base, src, "ambiguous reference", .{});
+ errdefer msg.destroy(gpa);
+ for (candidates.items) |candidate| {
+ const src_loc = candidate.srcLoc();
+ try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
+ }
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ },
+ }
+ } else if (namespace.decls.get(ident_name)) |decl| {
+ try mod.declareDeclDependency(sema.owner_decl, decl);
return decl;
}
+
log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{
sema.owner_decl, sema.owner_decl.name, ident_name, namespace_decl, namespace_decl.name,
});
// TODO This dependency is too strong. Really, it should only be a dependency
// on the non-existence of `ident_name` in the namespace. We can lessen the number of
// outdated declarations by making this dependency more sophisticated.
- try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);
+ try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
return null;
}
@@ -2727,10 +2798,7 @@ fn analyzeCall(
// we need to resolve the field type expressions right here, right now, while
// the child `Sema` is still available, with the AIR instruction map intact,
// because the field type expressions may reference into it.
- if (sema.typeOf(result).zigTypeTag() == .Type) {
- const ty = try sema.analyzeAsType(&child_block, call_src, result);
- try sema.resolveDeclFields(&child_block, call_src, ty);
- }
+ try sema.resolvePendingTypes(&child_block);
}
break :res2 result;
@@ -5332,6 +5400,7 @@ fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const src = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
@@ -5344,7 +5413,7 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
"expected struct, enum, union, or opaque, found '{}'",
.{container_type},
);
- if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
+ if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
if (decl.is_pub or decl.namespace.file_scope == block.base.namespace().file_scope) {
return Air.Inst.Ref.bool_true;
}
@@ -8203,7 +8272,7 @@ fn namespaceLookup(
) CompileError!?*Decl {
const mod = sema.mod;
const gpa = sema.gpa;
- if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
+ if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
if (!decl.is_pub and decl.namespace.file_scope != block.getFileScope()) {
const msg = msg: {
const msg = try mod.errMsg(&block.base, src, "'{s}' is not marked 'pub'", .{
@@ -8919,8 +8988,7 @@ fn analyzeDeclVal(
return result;
}
-fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
- try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void {
sema.mod.ensureDeclAnalyzed(decl) catch |err| {
if (sema.owner_func) |owner_func| {
owner_func.state = .dependency_failure;
@@ -8929,6 +8997,11 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
}
return err;
};
+}
+
+fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
+ try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+ try sema.ensureDeclAnalyzed(decl);
const decl_tv = try decl.typedValue();
if (decl_tv.val.castTag(.variable)) |payload| {
@@ -9560,6 +9633,16 @@ pub fn resolveTypeLayout(
}
}
+pub fn resolvePendingTypes(sema: *Sema, block: *Scope.Block) !void {
+ for (sema.types_pending_resolution.items) |ty| {
+ // If an error happens resolving the fields of a struct, it will be marked
+ // invalid and a proper compile error set up. But we should still look at the
+ // other types pending resolution.
+ const src: LazySrcLoc = .{ .node_offset = 0 };
+ sema.resolveDeclFields(block, src, ty) catch continue;
+ }
+}
+
/// `sema` and `block` are expected to be the same ones used for the `Decl`.
pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
switch (ty.tag()) {