Skip to content

Commit e68601d

Browse files
committed
compiler: accept pointer operand to @export
This commit does not yet migrate uses of this builtin. Resolves: #14911
1 parent 5132549 commit e68601d

File tree

5 files changed

+85
-200
lines changed

5 files changed

+85
-200
lines changed

lib/std/zig/AstGen.zig

Lines changed: 5 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,7 +2855,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28552855
.ensure_result_non_error,
28562856
.ensure_err_union_payload_void,
28572857
.@"export",
2858-
.export_value,
28592858
.set_eval_branch_quota,
28602859
.atomic_store,
28612860
.store_node,
@@ -9136,87 +9135,11 @@ fn builtinCall(
91369135
// zig fmt: on
91379136

91389137
.@"export" => {
9139-
const node_tags = tree.nodes.items(.tag);
9140-
const node_datas = tree.nodes.items(.data);
9141-
// This function causes a Decl to be exported. The first parameter is not an expression,
9142-
// but an identifier of the Decl to be exported.
9143-
var namespace: Zir.Inst.Ref = .none;
9144-
var decl_name: Zir.NullTerminatedString = .empty;
9145-
switch (node_tags[params[0]]) {
9146-
.identifier => {
9147-
const ident_token = main_tokens[params[0]];
9148-
if (isPrimitive(tree.tokenSlice(ident_token))) {
9149-
return astgen.failTok(ident_token, "unable to export primitive value", .{});
9150-
}
9151-
decl_name = try astgen.identAsString(ident_token);
9152-
9153-
var s = scope;
9154-
var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
9155-
while (true) switch (s.tag) {
9156-
.local_val => {
9157-
const local_val = s.cast(Scope.LocalVal).?;
9158-
if (local_val.name == decl_name) {
9159-
local_val.used = ident_token;
9160-
_ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9161-
.operand = local_val.inst,
9162-
.options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
9163-
});
9164-
return rvalue(gz, ri, .void_value, node);
9165-
}
9166-
s = local_val.parent;
9167-
},
9168-
.local_ptr => {
9169-
const local_ptr = s.cast(Scope.LocalPtr).?;
9170-
if (local_ptr.name == decl_name) {
9171-
if (!local_ptr.maybe_comptime)
9172-
return astgen.failNode(params[0], "unable to export runtime-known value", .{});
9173-
local_ptr.used = ident_token;
9174-
const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
9175-
_ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9176-
.operand = loaded,
9177-
.options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
9178-
});
9179-
return rvalue(gz, ri, .void_value, node);
9180-
}
9181-
s = local_ptr.parent;
9182-
},
9183-
.gen_zir => s = s.cast(GenZir).?.parent,
9184-
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
9185-
.namespace => {
9186-
const ns = s.cast(Scope.Namespace).?;
9187-
if (ns.decls.get(decl_name)) |i| {
9188-
if (found_already) |f| {
9189-
return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
9190-
try astgen.errNoteNode(f, "declared here", .{}),
9191-
try astgen.errNoteNode(i, "also declared here", .{}),
9192-
});
9193-
}
9194-
// We found a match but must continue looking for ambiguous references to decls.
9195-
found_already = i;
9196-
}
9197-
s = ns.parent;
9198-
},
9199-
.top => break,
9200-
};
9201-
if (found_already == null) {
9202-
const ident_name = try astgen.identifierTokenString(ident_token);
9203-
return astgen.failNode(params[0], "use of undeclared identifier '{s}'", .{ident_name});
9204-
}
9205-
},
9206-
.field_access => {
9207-
const namespace_node = node_datas[params[0]].lhs;
9208-
namespace = try typeExpr(gz, scope, namespace_node);
9209-
const dot_token = main_tokens[params[0]];
9210-
const field_ident = dot_token + 1;
9211-
decl_name = try astgen.identAsString(field_ident);
9212-
},
9213-
else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
9214-
}
9215-
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]);
9216-
_ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
9217-
.namespace = namespace,
9218-
.decl_name = decl_name,
9219-
.options = options,
9138+
const ptr_inst = try comptimeExpr(gz, scope, .{ .rl = .none }, params[0]);
9139+
const options_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]);
9140+
_ = try gz.addPlNode(.@"export", node, Zir.Inst.Bin{
9141+
.lhs = ptr_inst,
9142+
.rhs = options_inst,
92209143
});
92219144
return rvalue(gz, ri, .void_value, node);
92229145
},

lib/std/zig/Zir.zig

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -436,14 +436,10 @@ pub const Inst = struct {
436436
error_union_type,
437437
/// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
438438
error_value,
439-
/// Implements the `@export` builtin function, based on either an identifier to a Decl,
440-
/// or field access of a Decl. The thing being exported is the Decl.
441-
/// Uses the `pl_node` union field. Payload is `Export`.
439+
/// Implements the `@export` builtin function,
440+
/// Uses the `pl_node` union field. Payload is `Bin`.
441+
/// `lhs` is pointer to export, `rhs` is options.
442442
@"export",
443-
/// Implements the `@export` builtin function, based on a comptime-known value.
444-
/// The thing being exported is the comptime-known value which is the operand.
445-
/// Uses the `pl_node` union field. Payload is `ExportValue`.
446-
export_value,
447443
/// Given a pointer to a struct or object that contains virtual fields, returns a pointer
448444
/// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
449445
/// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
@@ -1103,7 +1099,6 @@ pub const Inst = struct {
11031099
.ensure_result_non_error,
11041100
.ensure_err_union_payload_void,
11051101
.@"export",
1106-
.export_value,
11071102
.field_ptr,
11081103
.field_val,
11091104
.field_ptr_named,
@@ -1325,7 +1320,6 @@ pub const Inst = struct {
13251320
.validate_deref,
13261321
.validate_destructure,
13271322
.@"export",
1328-
.export_value,
13291323
.set_runtime_safety,
13301324
.memcpy,
13311325
.memset,
@@ -1653,7 +1647,6 @@ pub const Inst = struct {
16531647
.error_union_type = .pl_node,
16541648
.error_value = .str_tok,
16551649
.@"export" = .pl_node,
1656-
.export_value = .pl_node,
16571650
.field_ptr = .pl_node,
16581651
.field_val = .pl_node,
16591652
.field_ptr_named = .pl_node,
@@ -3416,21 +3409,6 @@ pub const Inst = struct {
34163409
};
34173410
};
34183411

3419-
pub const Export = struct {
3420-
/// If present, this is referring to a Decl via field access, e.g. `a.b`.
3421-
/// If omitted, this is referring to a Decl via identifier, e.g. `a`.
3422-
namespace: Ref,
3423-
/// Null-terminated string index.
3424-
decl_name: NullTerminatedString,
3425-
options: Ref,
3426-
};
3427-
3428-
pub const ExportValue = struct {
3429-
/// The comptime value to export.
3430-
operand: Ref,
3431-
options: Ref,
3432-
};
3433-
34343412
/// Trailing: `CompileErrors.Item` for each `items_len`.
34353413
pub const CompileErrors = struct {
34363414
items_len: u32,

src/Module.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3781,7 +3781,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
37813781
const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) };
37823782
if (is_inline) return sema.fail(&block_scope, export_src, "export of inline function", .{});
37833783
// The scope needs to have the decl in it.
3784-
try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index);
3784+
try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, .{ .decl_index = decl_index });
37853785
}
37863786

37873787
return result;

src/Sema.zig

Lines changed: 75 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,11 +1409,6 @@ fn analyzeBodyInner(
14091409
i += 1;
14101410
continue;
14111411
},
1412-
.export_value => {
1413-
try sema.zirExportValue(block, inst);
1414-
i += 1;
1415-
continue;
1416-
},
14171412
.set_runtime_safety => {
14181413
try sema.zirSetRuntimeSafety(block, inst);
14191414
i += 1;
@@ -6358,85 +6353,89 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
63586353
const tracy = trace(@src());
63596354
defer tracy.end();
63606355

6361-
const mod = sema.mod;
6356+
const zcu = sema.mod;
63626357
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
6363-
const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
6358+
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
63646359
const src = inst_data.src();
63656360
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
63666361
const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
6367-
const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name));
6368-
const decl_index = if (extra.namespace != .none) index_blk: {
6369-
const container_ty = try sema.resolveType(block, operand_src, extra.namespace);
6370-
const container_namespace = container_ty.getNamespaceIndex(mod);
6371-
6372-
const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false);
6373-
break :index_blk maybe_index orelse
6374-
return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
6375-
} else try sema.lookupIdentifier(block, operand_src, decl_name);
6376-
const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) {
6377-
error.NeededSourceLocation => {
6378-
_ = try sema.resolveExportOptions(block, options_src, extra.options);
6379-
unreachable;
6380-
},
6381-
else => |e| return e,
6382-
};
6383-
{
6384-
try mod.ensureDeclAnalyzed(decl_index);
6385-
const exported_decl = mod.declPtr(decl_index);
6386-
if (exported_decl.val.getFunction(mod)) |function| {
6387-
return sema.analyzeExport(block, src, options, function.owner_decl);
6388-
}
6362+
6363+
const operand = try sema.resolveInst(extra.lhs);
6364+
const ptr_val = try sema.resolveConstDefinedValue(block, operand_src, operand, .{
6365+
.needed_comptime_reason = "export pointer must be comptime-known",
6366+
});
6367+
const ptr_ty = ptr_val.typeOf(zcu);
6368+
6369+
switch (ptr_ty.zigTypeTag(zcu)) {
6370+
.Pointer => if (ptr_ty.isSlice(zcu)) return sema.failWithOwnedErrorMsg(block, msg: {
6371+
const msg = try sema.errMsg(block, operand_src, "expected pointer, found '{}'", .{ptr_ty.fmt(zcu)});
6372+
errdefer msg.destroy(sema.gpa);
6373+
try sema.errNote(block, operand_src, msg, "use 'ptr' field to convert slice to many pointer", .{});
6374+
break :msg msg;
6375+
}),
6376+
else => return sema.failWithOwnedErrorMsg(block, msg: {
6377+
const msg = try sema.errMsg(block, operand_src, "expected pointer, found '{}'", .{ptr_ty.fmt(zcu)});
6378+
errdefer msg.destroy(sema.gpa);
6379+
try sema.errNote(block, operand_src, msg, "take the address of a value to export it", .{});
6380+
break :msg msg;
6381+
}),
63896382
}
6390-
try sema.analyzeExport(block, src, options, decl_index);
6391-
}
63926383

6393-
fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
6394-
const tracy = trace(@src());
6395-
defer tracy.end();
6384+
const options = try sema.resolveExportOptions(block, options_src, extra.rhs);
63966385

6397-
const mod = sema.mod;
6398-
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
6399-
const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
6400-
const src = inst_data.src();
6401-
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
6402-
const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
6403-
const operand = try sema.resolveInstConst(block, operand_src, extra.operand, .{
6404-
.needed_comptime_reason = "export target must be comptime-known",
6405-
});
6406-
const options = try sema.resolveExportOptions(block, options_src, extra.options);
6407-
if (options.linkage == .internal)
6408-
return;
6409-
if (operand.getFunction(mod)) |function| {
6410-
const decl_index = function.owner_decl;
6411-
return sema.analyzeExport(block, src, options, decl_index);
6386+
if (ptr_val.canMutateComptimeVarState(zcu)) {
6387+
return sema.failWithOwnedErrorMsg(block, msg: {
6388+
const msg = try sema.errMsg(block, operand_src, "export pointer contains reference to comptime var", .{});
6389+
errdefer msg.destroy(sema.gpa);
6390+
try sema.errNote(block, operand_src, msg, "comptime var pointers are not available at runtime", .{});
6391+
break :msg msg;
6392+
});
64126393
}
64136394

6414-
try addExport(mod, .{
6415-
.opts = options,
6416-
.src = src,
6417-
.owner_decl = sema.owner_decl_index,
6418-
.src_decl = block.src_decl,
6419-
.exported = .{ .value = operand.toIntern() },
6420-
.status = .in_progress,
6421-
});
6395+
// TODO: support exporting part of Decl, so always use Decl if `ptr_val.pointerDecl(zcu) != .none`
6396+
const exported: Module.Exported = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
6397+
.decl => |decl_index| .{ .decl_index = decl_index },
6398+
else => e: {
6399+
const val = try sema.pointerDeref(block, operand_src, ptr_val, ptr_val.typeOf(zcu)) orelse
6400+
return sema.failWithOwnedErrorMsg(block, msg: {
6401+
const msg = try sema.errMsg(block, operand_src, "export target is not comptime-known", .{});
6402+
errdefer msg.destroy(sema.gpa);
6403+
try sema.errNote(block, operand_src, msg, "export must refer to global declaration or comptime-known value", .{});
6404+
break :msg msg;
6405+
});
6406+
break :e .{ .value = val.toIntern() };
6407+
},
6408+
};
6409+
try sema.analyzeExport(block, src, options, exported);
64226410
}
64236411

64246412
pub fn analyzeExport(
64256413
sema: *Sema,
64266414
block: *Block,
64276415
src: LazySrcLoc,
64286416
options: Module.Export.Options,
6429-
exported_decl_index: InternPool.DeclIndex,
6417+
exported: Module.Exported,
64306418
) !void {
64316419
const gpa = sema.gpa;
64326420
const mod = sema.mod;
64336421

64346422
if (options.linkage == .internal)
64356423
return;
64366424

6437-
try mod.ensureDeclAnalyzed(exported_decl_index);
6438-
const exported_decl = mod.declPtr(exported_decl_index);
6439-
const export_ty = exported_decl.typeOf(mod);
6425+
const export_val = switch (exported) {
6426+
.decl_index => |decl_index| ty: {
6427+
try mod.ensureDeclAnalyzed(decl_index);
6428+
const exported_decl = mod.declPtr(decl_index);
6429+
break :ty exported_decl.val;
6430+
},
6431+
.value => |ip_index| Value.fromInterned(ip_index),
6432+
};
6433+
if (export_val.getFunction(mod)) |func| {
6434+
if (exported != .decl_index or exported.decl_index != func.owner_decl) {
6435+
return sema.analyzeExport(block, src, options, .{ .decl_index = func.owner_decl });
6436+
}
6437+
}
6438+
const export_ty = export_val.typeOf(mod);
64406439

64416440
if (!try sema.validateExternType(export_ty, .other)) {
64426441
const msg = msg: {
@@ -6452,19 +6451,29 @@ pub fn analyzeExport(
64526451
return sema.failWithOwnedErrorMsg(block, msg);
64536452
}
64546453

6455-
// TODO: some backends might support re-exporting extern decls
6456-
if (exported_decl.isExtern(mod)) {
6457-
return sema.fail(block, src, "export target cannot be extern", .{});
6454+
switch (exported) {
6455+
.decl_index => |decl_index| {
6456+
const exported_decl = mod.declPtr(decl_index);
6457+
// TODO: some backends might support re-exporting extern decls
6458+
if (exported_decl.isExtern(mod)) {
6459+
return sema.fail(block, src, "export target cannot be extern", .{});
6460+
}
6461+
},
6462+
.value => {},
64586463
}
64596464

6460-
try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);
6465+
if (mod.intern_pool.isFuncBody(export_val.toIntern()) and
6466+
try sema.fnHasRuntimeBits(export_ty))
6467+
{
6468+
try mod.ensureFuncBodyAnalysisQueued(export_val.toIntern());
6469+
}
64616470

64626471
try addExport(mod, .{
64636472
.opts = options,
64646473
.src = src,
64656474
.owner_decl = sema.owner_decl_index,
64666475
.src_decl = block.src_decl,
6467-
.exported = .{ .decl_index = exported_decl_index },
6476+
.exported = exported,
64686477
.status = .in_progress,
64696478
});
64706479
}

0 commit comments

Comments
 (0)