lang: qualified namespace members in value position + alias carry
Two coupled capabilities on the road to the std restructure (current/PLAN-STDLIB.md, issue 0114): 1. alias.Type.method() / alias.Type as a call head, alias.CONST, and alias.Enum.variant now resolve — previously only alias.fn() and type-position alias.Type worked. objectIsValue treats an alias-rooted field_access as a type head; the call path strips the alias to the existing Type.method machinery; lowerFieldAccess resolves alias.CONST pinned to the target module and alias.Enum.x as a typed enum literal; resolveTypeWithBindings resolves qualified type_exprs pinned to the target. 2. The carry rule: namespaceAliasTarget resolves an alias from the file's own edges first, then from DIRECT flat imports (one level), diagnosing two distinct carried targets as ambiguous. All qualified shapes work through a carried alias — the std.sx namespace tail (mem.GPA.init() etc.) is now expressible. Regression: examples/0831-modules-namespace-alias-carry.sx (direct + carried, all seven shapes).
This commit is contained in:
@@ -415,6 +415,65 @@ pub fn lowerFieldAccess(self: *Lowering, fa: *const ast.FieldAccess, span: ast.S
|
||||
return self.lowerErrorTagLiteral(fa.field, span);
|
||||
}
|
||||
|
||||
// Namespace-alias stripping in value position. The target module's
|
||||
// declarations register under their bare names, so `alias.Member`
|
||||
// re-enters as `Member` (`r.LIMIT`, and `r.Color` as the receiver of
|
||||
// `r.Color.green`); `alias.Type.field` re-enters as `Type.field`.
|
||||
if (self.namespaceRootedMember(fa.object)) |inner| {
|
||||
const root = fa.object.data.field_access.object.data.identifier.name;
|
||||
if (self.namespaceAliasTarget(root, span)) |target| {
|
||||
// Resolve the inner name as a TYPE in the target's context
|
||||
// (the alias edge authorizes the reach).
|
||||
const saved_src = self.current_source_file;
|
||||
self.setCurrentSourceFile(target.target_module_path);
|
||||
const ty = self.resolveNominalLeaf(inner, false, span);
|
||||
self.setCurrentSourceFile(saved_src);
|
||||
if (ty != .unresolved and !ty.isBuiltin()) {
|
||||
const info = self.module.types.get(ty);
|
||||
if (info == .@"enum" or info == .tagged_union) {
|
||||
// `alias.Enum.variant` — a typed enum literal.
|
||||
const synth = self.alloc.create(Node) catch null;
|
||||
if (synth) |n| {
|
||||
n.* = .{ .span = span, .data = .{ .enum_literal = .{ .name = fa.field } } };
|
||||
const saved_tt = self.target_type;
|
||||
self.target_type = ty;
|
||||
const ref = self.lowerExpr(n);
|
||||
self.target_type = saved_tt;
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
// `alias.Type.member` (struct constants etc.) — strip the alias;
|
||||
// the type's members register under the bare type name globally.
|
||||
const synth = self.alloc.create(Node) catch null;
|
||||
if (synth) |n| {
|
||||
n.* = .{ .span = fa.object.span, .data = .{ .identifier = .{ .name = inner } } };
|
||||
const stripped = ast.FieldAccess{ .object = n, .field = fa.field, .is_optional = fa.is_optional };
|
||||
return self.lowerFieldAccess(&stripped, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fa.object.data == .identifier) {
|
||||
const oname = fa.object.data.identifier.name;
|
||||
const shadowed = if (self.scope) |s| s.lookup(oname) != null else false;
|
||||
if (!shadowed and !self.program_index.global_names.contains(oname)) {
|
||||
if (self.namespaceAliasTarget(oname, span)) |target| {
|
||||
const synth = self.alloc.create(Node) catch null;
|
||||
if (synth) |n| {
|
||||
n.* = .{ .span = span, .data = .{ .identifier = .{ .name = fa.field } } };
|
||||
// Lower in the TARGET module's context: the alias edge
|
||||
// authorizes the member, so the bare-visibility gate must
|
||||
// judge it as the target's own name, not the caller's.
|
||||
const saved_src = self.current_source_file;
|
||||
self.setCurrentSourceFile(target.target_module_path);
|
||||
const ref = self.lowerExpr(n);
|
||||
self.setCurrentSourceFile(saved_src);
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pack-arity intercept: `<pack_name>.len` in a pack-fn mono's
|
||||
// body resolves to the comptime-known N. The mono doesn't
|
||||
// materialise the `[]Any` slice that the inline path used, so
|
||||
|
||||
Reference in New Issue
Block a user