mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
commit
48e2ba3b3c
@ -7468,7 +7468,7 @@ fn localVarRef(
|
||||
}
|
||||
|
||||
// Can't close over a runtime variable
|
||||
if (num_namespaces_out != 0 and !local_ptr.maybe_comptime) {
|
||||
if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
|
||||
const ident_name = try astgen.identifierTokenString(ident_token);
|
||||
return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
|
||||
try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
|
||||
@ -8041,6 +8041,7 @@ fn typeOf(
|
||||
|
||||
var typeof_scope = gz.makeSubBlock(scope);
|
||||
typeof_scope.is_comptime = false;
|
||||
typeof_scope.is_typeof = true;
|
||||
typeof_scope.c_import = false;
|
||||
defer typeof_scope.unstack();
|
||||
|
||||
@ -10882,6 +10883,9 @@ const GenZir = struct {
|
||||
/// whenever we know Sema will analyze the current block with `is_comptime`,
|
||||
/// for instance when we're within a `struct_decl` or a `block_comptime`.
|
||||
is_comptime: bool,
|
||||
/// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
|
||||
/// variables is permitted where it is usually not.
|
||||
is_typeof: bool = false,
|
||||
/// This is set to true for inline loops; false otherwise.
|
||||
is_inline: bool = false,
|
||||
c_import: bool = false,
|
||||
@ -10953,6 +10957,7 @@ const GenZir = struct {
|
||||
fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
|
||||
return .{
|
||||
.is_comptime = gz.is_comptime,
|
||||
.is_typeof = gz.is_typeof,
|
||||
.c_import = gz.c_import,
|
||||
.decl_node_index = gz.decl_node_index,
|
||||
.decl_line = gz.decl_line,
|
||||
|
||||
@ -5524,22 +5524,38 @@ const MacroCtx = struct {
|
||||
return MacroSlicer{ .source = self.source, .tokens = self.list };
|
||||
}
|
||||
|
||||
fn containsUndefinedIdentifier(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?[]const u8 {
|
||||
const MacroTranslateError = union(enum) {
|
||||
undefined_identifier: []const u8,
|
||||
invalid_arg_usage: []const u8,
|
||||
};
|
||||
|
||||
fn checkTranslatableMacro(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?MacroTranslateError {
|
||||
const slicer = self.makeSlicer();
|
||||
var last_is_type_kw = false;
|
||||
var i: usize = 1; // index 0 is the macro name
|
||||
while (i < self.list.len) : (i += 1) {
|
||||
const token = self.list[i];
|
||||
switch (token.id) {
|
||||
.Period, .Arrow => i += 1, // skip next token since field identifiers can be unknown
|
||||
.Keyword_struct, .Keyword_union, .Keyword_enum => if (!last_is_type_kw) {
|
||||
last_is_type_kw = true;
|
||||
continue;
|
||||
},
|
||||
.Identifier => {
|
||||
const identifier = slicer.slice(token);
|
||||
const is_param = for (params) |param| {
|
||||
if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true;
|
||||
} else false;
|
||||
if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) return identifier;
|
||||
if (is_param and last_is_type_kw) {
|
||||
return .{ .invalid_arg_usage = identifier };
|
||||
}
|
||||
if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) {
|
||||
return .{ .undefined_identifier = identifier };
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
last_is_type_kw = false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -5649,8 +5665,10 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
|
||||
fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
|
||||
const scope = &c.global_scope.base;
|
||||
|
||||
if (m.containsUndefinedIdentifier(scope, &.{})) |ident|
|
||||
return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});
|
||||
if (m.checkTranslatableMacro(scope, &.{})) |err| switch (err) {
|
||||
.undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
|
||||
.invalid_arg_usage => unreachable, // no args
|
||||
};
|
||||
|
||||
const init_node = try parseCExpr(c, m, scope);
|
||||
const last = m.next().?;
|
||||
@ -5698,8 +5716,10 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
|
||||
|
||||
try m.skip(c, .RParen);
|
||||
|
||||
if (m.containsUndefinedIdentifier(scope, fn_params.items)) |ident|
|
||||
return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident});
|
||||
if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) {
|
||||
.undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
|
||||
.invalid_arg_usage => |ident| return m.fail(c, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}),
|
||||
};
|
||||
|
||||
const expr = try parseCExpr(c, m, scope);
|
||||
const last = m.next().?;
|
||||
|
||||
@ -991,6 +991,16 @@ test "closure capture type of runtime-known parameter" {
|
||||
try S.b(c);
|
||||
}
|
||||
|
||||
test "closure capture type of runtime-known var" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
|
||||
var x: u32 = 1234;
|
||||
const S = struct { val: @TypeOf(x + 100) };
|
||||
const s: S = .{ .val = x };
|
||||
try expect(s.val == 1234);
|
||||
}
|
||||
|
||||
test "comptime break passing through runtime condition converted to runtime break" {
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
|
||||
@ -1895,4 +1895,14 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
|
||||
cases.add("Closure over local in typeof",
|
||||
\\#include <stdlib.h>
|
||||
\\int main(void) {
|
||||
\\ int x = 123;
|
||||
\\ union { typeof(x) val; } u = { x };
|
||||
\\ if (u.val != 123) abort();
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
@ -4129,4 +4129,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ }) != 0) {}
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("macro using argument as struct name is not translated",
|
||||
\\#define FOO(x) struct x
|
||||
, &[_][]const u8{
|
||||
\\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`");
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user