mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
Part of #19063. Primarily, this moves Aro from deps/ to lib/compiler/ so that it can be lazily compiled from source. src/aro_translate_c.zig is moved to lib/compiler/aro_translate_c.zig and some of Zig CLI logic moved to a main() function there. aro_translate_c.zig becomes the "common" import for clang-based translate-c. Not all of the compiler was able to be detangled from Aro, however, so it still, for now, remains being compiled with the main compiler sources due to the clang-based translate-c depending on it. Once aro-based translate-c achieves feature parity with the clang-based translate-c implementation, the clang-based one can be removed from Zig. Aro made it unnecessarily difficult to depend on with these .def files and all these Zig module requirements. I looked at the .def files and made these observations: - The canonical source is llvm .def files. - Therefore there is an update process to sync with llvm that involves regenerating the .def files in Aro. - Therefore you might as well just regenerate the .zig files directly and check those into Aro. - Also with a small amount of tinkering, the file size on disk of these generated .zig files can be made many times smaller, without compromising type safety in the usage of the data. This would make things much easier on Zig as downstream project, particularly we could remove those pesky stubs when bootstrapping. I have gone ahead with these changes since they unblock me and I will have a chat with Vexu to see what he thinks.
393 lines
12 KiB
Zig
393 lines
12 KiB
Zig
const std = @import("std");
|
|
const mem = std.mem;
|
|
const Allocator = mem.Allocator;
|
|
const assert = std.debug.assert;
|
|
const Tree = @import("Tree.zig");
|
|
const Token = Tree.Token;
|
|
const TokenIndex = Tree.TokenIndex;
|
|
const NodeIndex = Tree.NodeIndex;
|
|
const Type = @import("Type.zig");
|
|
const Parser = @import("Parser.zig");
|
|
const Value = @import("Value.zig");
|
|
const StringId = @import("StringInterner.zig").StringId;
|
|
|
|
const SymbolStack = @This();
|
|
|
|
pub const Symbol = struct {
|
|
name: StringId,
|
|
ty: Type,
|
|
tok: TokenIndex,
|
|
node: NodeIndex = .none,
|
|
kind: Kind,
|
|
val: Value,
|
|
};
|
|
|
|
pub const Kind = enum {
|
|
typedef,
|
|
@"struct",
|
|
@"union",
|
|
@"enum",
|
|
decl,
|
|
def,
|
|
enumeration,
|
|
constexpr,
|
|
};
|
|
|
|
scopes: std.ArrayListUnmanaged(Scope) = .{},
|
|
/// allocations from nested scopes are retained after popping; `active_len` is the number
|
|
/// of currently-active items in `scopes`.
|
|
active_len: usize = 0,
|
|
|
|
const Scope = struct {
|
|
vars: std.AutoHashMapUnmanaged(StringId, Symbol) = .{},
|
|
tags: std.AutoHashMapUnmanaged(StringId, Symbol) = .{},
|
|
|
|
fn deinit(self: *Scope, allocator: Allocator) void {
|
|
self.vars.deinit(allocator);
|
|
self.tags.deinit(allocator);
|
|
}
|
|
|
|
fn clearRetainingCapacity(self: *Scope) void {
|
|
self.vars.clearRetainingCapacity();
|
|
self.tags.clearRetainingCapacity();
|
|
}
|
|
};
|
|
|
|
pub fn deinit(s: *SymbolStack, gpa: Allocator) void {
|
|
std.debug.assert(s.active_len == 0); // all scopes should have been popped
|
|
for (s.scopes.items) |*scope| {
|
|
scope.deinit(gpa);
|
|
}
|
|
s.scopes.deinit(gpa);
|
|
s.* = undefined;
|
|
}
|
|
|
|
pub fn pushScope(s: *SymbolStack, p: *Parser) !void {
|
|
if (s.active_len + 1 > s.scopes.items.len) {
|
|
try s.scopes.append(p.gpa, .{});
|
|
s.active_len = s.scopes.items.len;
|
|
} else {
|
|
s.scopes.items[s.active_len].clearRetainingCapacity();
|
|
s.active_len += 1;
|
|
}
|
|
}
|
|
|
|
pub fn popScope(s: *SymbolStack) void {
|
|
s.active_len -= 1;
|
|
}
|
|
|
|
pub fn findTypedef(s: *SymbolStack, p: *Parser, name: StringId, name_tok: TokenIndex, no_type_yet: bool) !?Symbol {
|
|
const prev = s.lookup(name, .vars) orelse s.lookup(name, .tags) orelse return null;
|
|
switch (prev.kind) {
|
|
.typedef => return prev,
|
|
.@"struct" => {
|
|
if (no_type_yet) return null;
|
|
try p.errStr(.must_use_struct, name_tok, p.tokSlice(name_tok));
|
|
return prev;
|
|
},
|
|
.@"union" => {
|
|
if (no_type_yet) return null;
|
|
try p.errStr(.must_use_union, name_tok, p.tokSlice(name_tok));
|
|
return prev;
|
|
},
|
|
.@"enum" => {
|
|
if (no_type_yet) return null;
|
|
try p.errStr(.must_use_enum, name_tok, p.tokSlice(name_tok));
|
|
return prev;
|
|
},
|
|
else => return null,
|
|
}
|
|
}
|
|
|
|
pub fn findSymbol(s: *SymbolStack, name: StringId) ?Symbol {
|
|
return s.lookup(name, .vars);
|
|
}
|
|
|
|
pub fn findTag(
|
|
s: *SymbolStack,
|
|
p: *Parser,
|
|
name: StringId,
|
|
kind: Token.Id,
|
|
name_tok: TokenIndex,
|
|
next_tok_id: Token.Id,
|
|
) !?Symbol {
|
|
// `tag Name;` should always result in a new type if in a new scope.
|
|
const prev = (if (next_tok_id == .semicolon) s.get(name, .tags) else s.lookup(name, .tags)) orelse return null;
|
|
switch (prev.kind) {
|
|
.@"enum" => if (kind == .keyword_enum) return prev,
|
|
.@"struct" => if (kind == .keyword_struct) return prev,
|
|
.@"union" => if (kind == .keyword_union) return prev,
|
|
else => unreachable,
|
|
}
|
|
if (s.get(name, .tags) == null) return null;
|
|
try p.errStr(.wrong_tag, name_tok, p.tokSlice(name_tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
return null;
|
|
}
|
|
|
|
const ScopeKind = enum {
|
|
/// structs, enums, unions
|
|
tags,
|
|
/// everything else
|
|
vars,
|
|
};
|
|
|
|
/// Return the Symbol for `name` (or null if not found) in the innermost scope
|
|
pub fn get(s: *SymbolStack, name: StringId, kind: ScopeKind) ?Symbol {
|
|
return switch (kind) {
|
|
.vars => s.scopes.items[s.active_len - 1].vars.get(name),
|
|
.tags => s.scopes.items[s.active_len - 1].tags.get(name),
|
|
};
|
|
}
|
|
|
|
/// Return the Symbol for `name` (or null if not found) in the nearest active scope,
|
|
/// starting at the innermost.
|
|
fn lookup(s: *SymbolStack, name: StringId, kind: ScopeKind) ?Symbol {
|
|
var i = s.active_len;
|
|
while (i > 0) {
|
|
i -= 1;
|
|
switch (kind) {
|
|
.vars => if (s.scopes.items[i].vars.get(name)) |sym| return sym,
|
|
.tags => if (s.scopes.items[i].tags.get(name)) |sym| return sym,
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Define a symbol in the innermost scope. Does not issue diagnostics or check correctness
|
|
/// with regard to the C standard.
|
|
pub fn define(s: *SymbolStack, allocator: Allocator, symbol: Symbol) !void {
|
|
switch (symbol.kind) {
|
|
.constexpr, .def, .decl, .enumeration, .typedef => {
|
|
try s.scopes.items[s.active_len - 1].vars.put(allocator, symbol.name, symbol);
|
|
},
|
|
.@"struct", .@"union", .@"enum" => {
|
|
try s.scopes.items[s.active_len - 1].tags.put(allocator, symbol.name, symbol);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn defineTypedef(
|
|
s: *SymbolStack,
|
|
p: *Parser,
|
|
name: StringId,
|
|
ty: Type,
|
|
tok: TokenIndex,
|
|
node: NodeIndex,
|
|
) !void {
|
|
if (s.get(name, .vars)) |prev| {
|
|
switch (prev.kind) {
|
|
.typedef => {
|
|
if (!ty.eql(prev.ty, p.comp, true)) {
|
|
try p.errStr(.redefinition_of_typedef, tok, try p.typePairStrExtra(ty, " vs ", prev.ty));
|
|
if (prev.tok != 0) try p.errTok(.previous_definition, prev.tok);
|
|
}
|
|
},
|
|
.enumeration, .decl, .def, .constexpr => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
try s.define(p.gpa, .{
|
|
.kind = .typedef,
|
|
.name = name,
|
|
.tok = tok,
|
|
.ty = ty,
|
|
.node = node,
|
|
.val = .{},
|
|
});
|
|
}
|
|
|
|
pub fn defineSymbol(
|
|
s: *SymbolStack,
|
|
p: *Parser,
|
|
name: StringId,
|
|
ty: Type,
|
|
tok: TokenIndex,
|
|
node: NodeIndex,
|
|
val: Value,
|
|
constexpr: bool,
|
|
) !void {
|
|
if (s.get(name, .vars)) |prev| {
|
|
switch (prev.kind) {
|
|
.enumeration => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
.decl => {
|
|
if (!ty.eql(prev.ty, p.comp, true)) {
|
|
try p.errStr(.redefinition_incompatible, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
}
|
|
},
|
|
.def, .constexpr => {
|
|
try p.errStr(.redefinition, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
.typedef => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
try s.define(p.gpa, .{
|
|
.kind = if (constexpr) .constexpr else .def,
|
|
.name = name,
|
|
.tok = tok,
|
|
.ty = ty,
|
|
.node = node,
|
|
.val = val,
|
|
});
|
|
}
|
|
|
|
/// Get a pointer to the named symbol in the innermost scope.
|
|
/// Asserts that a symbol with the name exists.
|
|
pub fn getPtr(s: *SymbolStack, name: StringId, kind: ScopeKind) *Symbol {
|
|
return switch (kind) {
|
|
.tags => s.scopes.items[s.active_len - 1].tags.getPtr(name).?,
|
|
.vars => s.scopes.items[s.active_len - 1].vars.getPtr(name).?,
|
|
};
|
|
}
|
|
|
|
pub fn declareSymbol(
|
|
s: *SymbolStack,
|
|
p: *Parser,
|
|
name: StringId,
|
|
ty: Type,
|
|
tok: TokenIndex,
|
|
node: NodeIndex,
|
|
) !void {
|
|
if (s.get(name, .vars)) |prev| {
|
|
switch (prev.kind) {
|
|
.enumeration => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
.decl => {
|
|
if (!ty.eql(prev.ty, p.comp, true)) {
|
|
try p.errStr(.redefinition_incompatible, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
}
|
|
},
|
|
.def, .constexpr => {
|
|
if (!ty.eql(prev.ty, p.comp, true)) {
|
|
try p.errStr(.redefinition_incompatible, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
} else {
|
|
return;
|
|
}
|
|
},
|
|
.typedef => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
try s.define(p.gpa, .{
|
|
.kind = .decl,
|
|
.name = name,
|
|
.tok = tok,
|
|
.ty = ty,
|
|
.node = node,
|
|
.val = .{},
|
|
});
|
|
}
|
|
|
|
pub fn defineParam(s: *SymbolStack, p: *Parser, name: StringId, ty: Type, tok: TokenIndex) !void {
|
|
if (s.get(name, .vars)) |prev| {
|
|
switch (prev.kind) {
|
|
.enumeration, .decl, .def, .constexpr => {
|
|
try p.errStr(.redefinition_of_parameter, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
.typedef => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
if (ty.is(.fp16) and !p.comp.hasHalfPrecisionFloatABI()) {
|
|
try p.errStr(.suggest_pointer_for_invalid_fp16, tok, "parameters");
|
|
}
|
|
try s.define(p.gpa, .{
|
|
.kind = .def,
|
|
.name = name,
|
|
.tok = tok,
|
|
.ty = ty,
|
|
.val = .{},
|
|
});
|
|
}
|
|
|
|
pub fn defineTag(
|
|
s: *SymbolStack,
|
|
p: *Parser,
|
|
name: StringId,
|
|
kind: Token.Id,
|
|
tok: TokenIndex,
|
|
) !?Symbol {
|
|
const prev = s.get(name, .tags) orelse return null;
|
|
switch (prev.kind) {
|
|
.@"enum" => {
|
|
if (kind == .keyword_enum) return prev;
|
|
try p.errStr(.wrong_tag, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
return null;
|
|
},
|
|
.@"struct" => {
|
|
if (kind == .keyword_struct) return prev;
|
|
try p.errStr(.wrong_tag, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
return null;
|
|
},
|
|
.@"union" => {
|
|
if (kind == .keyword_union) return prev;
|
|
try p.errStr(.wrong_tag, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
return null;
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn defineEnumeration(
|
|
s: *SymbolStack,
|
|
p: *Parser,
|
|
name: StringId,
|
|
ty: Type,
|
|
tok: TokenIndex,
|
|
val: Value,
|
|
) !void {
|
|
if (s.get(name, .vars)) |prev| {
|
|
switch (prev.kind) {
|
|
.enumeration => {
|
|
try p.errStr(.redefinition, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
return;
|
|
},
|
|
.decl, .def, .constexpr => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
return;
|
|
},
|
|
.typedef => {
|
|
try p.errStr(.redefinition_different_sym, tok, p.tokSlice(tok));
|
|
try p.errTok(.previous_definition, prev.tok);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
try s.define(p.gpa, .{
|
|
.kind = .enumeration,
|
|
.name = name,
|
|
.tok = tok,
|
|
.ty = ty,
|
|
.val = val,
|
|
});
|
|
}
|