authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-05-28 00:48:09+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-10-01 23:51:54+03:00
log7ec729b3ae9ea368124592d8ef1cef422c51de3b
tree585c547065d982e21a1923de6a8d41adc9688f9d
parent31ecf75311f83efb0bc0f108c3a3fb6b4c64e153

aro-translate-c: move shared types to a common namespace


3 files changed, 335 insertions(+), 599 deletions(-)

src/aro_translate_c.zig+11-293
......@@ -10,305 +10,23 @@ const Type = aro.Type;
1010const ast = @import("translate_c/ast.zig");
1111const ZigNode = ast.Node;
1212const ZigTag = ZigNode.Tag;
13const common = @import("translate_c/common.zig");
14const Error = common.Error;
15const MacroProcessingError = common.MacroProcessingError;
16const TypeError = common.TypeError;
17const TransError = common.TransError;
18const SymbolTable = common.SymbolTable;
19const AliasList = common.AliasList;
20const ResultUsed = common.ResultUsed;
21const Scope = common.ScopeExtra(Context, Type);
1322
14const Error = mem.Allocator.Error;
15const TransError = translate_c.TransError;
16const TypeError = translate_c.TypeError;
17const ResultUsed = translate_c.ResultUsed;
18const AliasList = translate_c.AliasList;
19const SymbolTable = translate_c.SymbolTable;
2023pub const Compilation = aro.Compilation;
2124
22const Scope = struct {
23 id: Id,
24 parent: ?*Scope,
25
26 const Id = enum {
27 block,
28 root,
29 condition,
30 loop,
31 do_loop,
32 };
33
34 /// Used for the scope of condition expressions, for example `if (cond)`.
35 /// The block is lazily initialised because it is only needed for rare
36 /// cases of comma operators being used.
37 const Condition = struct {
38 base: Scope,
39 block: ?Block = null,
40
41 fn getBlockScope(self: *Condition, c: *Context) !*Block {
42 if (self.block) |*b| return b;
43 self.block = try Block.init(c, &self.base, true);
44 return &self.block.?;
45 }
46
47 fn deinit(self: *Condition) void {
48 if (self.block) |*b| b.deinit();
49 }
50 };
51
52 /// Represents an in-progress ZigNode.Block. This struct is stack-allocated.
53 /// When it is deinitialized, it produces an ZigNode.Block which is allocated
54 /// into the main arena.
55 const Block = struct {
56 base: Scope,
57 statements: std.ArrayList(ZigNode),
58 variables: AliasList,
59 mangle_count: u32 = 0,
60 label: ?[]const u8 = null,
61
62 /// By default all variables are discarded, since we do not know in advance if they
63 /// will be used. This maps the variable's name to the Discard payload, so that if
64 /// the variable is subsequently referenced we can indicate that the discard should
65 /// be skipped during the intermediate AST -> Zig AST render step.
66 variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
67
68 /// When the block corresponds to a function, keep track of the return type
69 /// so that the return expression can be cast, if necessary
70 return_type: ?Type = null,
71
72 /// C static local variables are wrapped in a block-local struct. The struct
73 /// is named after the (mangled) variable name, the Zig variable within the
74 /// struct itself is given this name.
75 const StaticInnerName = "static";
76
77 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
78 var blk = Block{
79 .base = .{
80 .id = .block,
81 .parent = parent,
82 },
83 .statements = std.ArrayList(ZigNode).init(c.gpa),
84 .variables = AliasList.init(c.gpa),
85 .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
86 };
87 if (labeled) {
88 blk.label = try blk.makeMangledName(c, "blk");
89 }
90 return blk;
91 }
92
93 fn deinit(self: *Block) void {
94 self.statements.deinit();
95 self.variables.deinit();
96 self.variable_discards.deinit();
97 self.* = undefined;
98 }
99
100 fn complete(self: *Block, c: *Context) !ZigNode {
101 if (self.base.parent.?.id == .do_loop) {
102 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
103 // do while, we want to put `if (cond) break;` at the end.
104 const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop);
105 var stmts = try c.arena.alloc(ZigNode, alloc_len);
106 stmts.len = self.statements.items.len;
107 @memcpy(stmts[0..self.statements.items.len], self.statements.items);
108 return ZigTag.block.create(c.arena, .{
109 .label = self.label,
110 .stmts = stmts,
111 });
112 }
113 if (self.statements.items.len == 0) return ZigTag.empty_block.init();
114 return ZigTag.block.create(c.arena, .{
115 .label = self.label,
116 .stmts = try c.arena.dupe(ZigNode, self.statements.items),
117 });
118 }
119
120 /// Given the desired name, return a name that does not shadow anything from outer scopes.
121 /// Inserts the returned name into the scope.
122 /// The name will not be visible to callers of getAlias.
123 fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
124 return scope.createMangledName(c, name, true);
125 }
126
127 /// Same as reserveMangledName, but enables the alias immediately.
128 fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
129 return scope.createMangledName(c, name, false);
130 }
131
132 fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
133 const name_copy = try c.arena.dupe(u8, name);
134 var proposed_name = name_copy;
135 while (scope.contains(proposed_name)) {
136 scope.mangle_count += 1;
137 proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
138 }
139 const new_mangle = try scope.variables.addOne();
140 if (reservation) {
141 new_mangle.* = .{ .name = name_copy, .alias = name_copy };
142 } else {
143 new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
144 }
145 return proposed_name;
146 }
147
148 fn getAlias(scope: *Block, name: []const u8) []const u8 {
149 for (scope.variables.items) |p| {
150 if (mem.eql(u8, p.name, name))
151 return p.alias;
152 }
153 return scope.base.parent.?.getAlias(name);
154 }
155
156 fn localContains(scope: *Block, name: []const u8) bool {
157 for (scope.variables.items) |p| {
158 if (mem.eql(u8, p.alias, name))
159 return true;
160 }
161 return false;
162 }
163
164 fn contains(scope: *Block, name: []const u8) bool {
165 if (scope.localContains(name))
166 return true;
167 return scope.base.parent.?.contains(name);
168 }
169
170 fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
171 const name_node = try ZigTag.identifier.create(c.arena, name);
172 const discard = try ZigTag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
173 try scope.statements.append(discard);
174 try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
175 }
176 };
177
178 const Root = struct {
179 base: Scope,
180 sym_table: SymbolTable,
181 macro_table: SymbolTable,
182 context: *Context,
183 nodes: std.ArrayList(ZigNode),
184
185 fn init(c: *Context) Root {
186 return .{
187 .base = .{
188 .id = .root,
189 .parent = null,
190 },
191 .sym_table = SymbolTable.init(c.gpa),
192 .macro_table = SymbolTable.init(c.gpa),
193 .context = c,
194 .nodes = std.ArrayList(ZigNode).init(c.gpa),
195 };
196 }
197
198 fn deinit(scope: *Root) void {
199 scope.sym_table.deinit();
200 scope.macro_table.deinit();
201 scope.nodes.deinit();
202 }
203
204 /// Check if the global scope contains this name, without looking into the "future", e.g.
205 /// ignore the preprocessed decl and macro names.
206 fn containsNow(scope: *Root, name: []const u8) bool {
207 return scope.sym_table.contains(name) or scope.macro_table.contains(name);
208 }
209
210 /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
211 fn contains(scope: *Root, name: []const u8) bool {
212 return scope.containsNow(name) or scope.context.global_names.contains(name);
213 }
214 };
215
216 fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
217 var scope = inner;
218 while (true) {
219 switch (scope.id) {
220 .root => unreachable,
221 .block => return @fieldParentPtr(Block, "base", scope),
222 .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
223 else => scope = scope.parent.?,
224 }
225 }
226 }
227
228 fn findBlockReturnType(inner: *Scope) Type {
229 var scope = inner;
230 while (true) {
231 switch (scope.id) {
232 .root => unreachable,
233 .block => {
234 const block = @fieldParentPtr(Block, "base", scope);
235 if (block.return_type) |qt| return qt;
236 scope = scope.parent.?;
237 },
238 else => scope = scope.parent.?,
239 }
240 }
241 }
242
243 fn getAlias(scope: *Scope, name: []const u8) []const u8 {
244 return switch (scope.id) {
245 .root => return name,
246 .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
247 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
248 };
249 }
250
251 fn contains(scope: *Scope, name: []const u8) bool {
252 return switch (scope.id) {
253 .root => @fieldParentPtr(Root, "base", scope).contains(name),
254 .block => @fieldParentPtr(Block, "base", scope).contains(name),
255 .loop, .do_loop, .condition => scope.parent.?.contains(name),
256 };
257 }
258
259 fn getBreakableScope(inner: *Scope) *Scope {
260 var scope = inner;
261 while (true) {
262 switch (scope.id) {
263 .root => unreachable,
264 .loop, .do_loop => return scope,
265 else => scope = scope.parent.?,
266 }
267 }
268 }
269
270 /// Appends a node to the first block scope if inside a function, or to the root tree if not.
271 fn appendNode(inner: *Scope, node: ZigNode) !void {
272 var scope = inner;
273 while (true) {
274 switch (scope.id) {
275 .root => {
276 const root = @fieldParentPtr(Root, "base", scope);
277 return root.nodes.append(node);
278 },
279 .block => {
280 const block = @fieldParentPtr(Block, "base", scope);
281 return block.statements.append(node);
282 },
283 else => scope = scope.parent.?,
284 }
285 }
286 }
287
288 fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
289 var scope = inner;
290 while (true) {
291 switch (scope.id) {
292 .root => return,
293 .block => {
294 const block = @fieldParentPtr(Block, "base", scope);
295 if (block.variable_discards.get(name)) |discard| {
296 discard.data.should_skip = true;
297 return;
298 }
299 },
300 else => {},
301 }
302 scope = scope.parent.?;
303 }
304 }
305};
306
30725const Context = struct {
30826 gpa: mem.Allocator,
30927 arena: mem.Allocator,
31028 decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
311 alias_list: translate_c.AliasList,
29 alias_list: AliasList,
31230 global_scope: *Scope.Root,
31331 mangle_count: u32 = 0,
31432 /// Table of record decls that have been demoted to opaques.
......@@ -429,7 +147,7 @@ pub fn translate(
429147 var context = Context{
430148 .gpa = gpa,
431149 .arena = arena,
432 .alias_list = translate_c.AliasList.init(gpa),
150 .alias_list = AliasList.init(gpa),
433151 .global_scope = try arena.create(Scope.Root),
434152 .pattern_list = try translate_c.PatternList.init(gpa),
435153 .comp = comp,
src/translate_c.zig+13-306
......@@ -7,312 +7,24 @@ const CToken = std.c.Token;
77const mem = std.mem;
88const math = std.math;
99const meta = std.meta;
10const CallingConvention = std.builtin.CallingConvention;
1011const ast = @import("translate_c/ast.zig");
1112const Node = ast.Node;
1213const Tag = Node.Tag;
13
14const CallingConvention = std.builtin.CallingConvention;
15
16pub const Error = std.mem.Allocator.Error;
17pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
18pub const TypeError = Error || error{UnsupportedType};
19pub const TransError = TypeError || error{UnsupportedTranslation};
20
21pub const SymbolTable = std.StringArrayHashMap(Node);
22pub const AliasList = std.ArrayList(struct {
23 alias: []const u8,
24 name: []const u8,
25});
14const common = @import("translate_c/common.zig");
15const Error = common.Error;
16const MacroProcessingError = common.MacroProcessingError;
17const TypeError = common.TypeError;
18const TransError = common.TransError;
19const SymbolTable = common.SymbolTable;
20const AliasList = common.AliasList;
21const ResultUsed = common.ResultUsed;
22const Scope = common.ScopeExtra(Context, clang.QualType);
2623
2724// Maps macro parameter names to token position, for determining if different
2825// identifiers refer to the same positional argument in different macros.
2926const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
3027
31const Scope = struct {
32 id: Id,
33 parent: ?*Scope,
34
35 const Id = enum {
36 block,
37 root,
38 condition,
39 loop,
40 do_loop,
41 };
42
43 /// Used for the scope of condition expressions, for example `if (cond)`.
44 /// The block is lazily initialised because it is only needed for rare
45 /// cases of comma operators being used.
46 const Condition = struct {
47 base: Scope,
48 block: ?Block = null,
49
50 fn getBlockScope(self: *Condition, c: *Context) !*Block {
51 if (self.block) |*b| return b;
52 self.block = try Block.init(c, &self.base, true);
53 return &self.block.?;
54 }
55
56 fn deinit(self: *Condition) void {
57 if (self.block) |*b| b.deinit();
58 }
59 };
60
61 /// Represents an in-progress Node.Block. This struct is stack-allocated.
62 /// When it is deinitialized, it produces an Node.Block which is allocated
63 /// into the main arena.
64 const Block = struct {
65 base: Scope,
66 statements: std.ArrayList(Node),
67 variables: AliasList,
68 mangle_count: u32 = 0,
69 label: ?[]const u8 = null,
70
71 /// By default all variables are discarded, since we do not know in advance if they
72 /// will be used. This maps the variable's name to the Discard payload, so that if
73 /// the variable is subsequently referenced we can indicate that the discard should
74 /// be skipped during the intermediate AST -> Zig AST render step.
75 variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
76
77 /// When the block corresponds to a function, keep track of the return type
78 /// so that the return expression can be cast, if necessary
79 return_type: ?clang.QualType = null,
80
81 /// C static local variables are wrapped in a block-local struct. The struct
82 /// is named after the (mangled) variable name, the Zig variable within the
83 /// struct itself is given this name.
84 const StaticInnerName = "static";
85
86 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
87 var blk = Block{
88 .base = .{
89 .id = .block,
90 .parent = parent,
91 },
92 .statements = std.ArrayList(Node).init(c.gpa),
93 .variables = AliasList.init(c.gpa),
94 .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
95 };
96 if (labeled) {
97 blk.label = try blk.makeMangledName(c, "blk");
98 }
99 return blk;
100 }
101
102 fn deinit(self: *Block) void {
103 self.statements.deinit();
104 self.variables.deinit();
105 self.variable_discards.deinit();
106 self.* = undefined;
107 }
108
109 fn complete(self: *Block, c: *Context) !Node {
110 if (self.base.parent.?.id == .do_loop) {
111 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
112 // do while, we want to put `if (cond) break;` at the end.
113 const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop);
114 var stmts = try c.arena.alloc(Node, alloc_len);
115 stmts.len = self.statements.items.len;
116 @memcpy(stmts[0..self.statements.items.len], self.statements.items);
117 return Tag.block.create(c.arena, .{
118 .label = self.label,
119 .stmts = stmts,
120 });
121 }
122 if (self.statements.items.len == 0) return Tag.empty_block.init();
123 return Tag.block.create(c.arena, .{
124 .label = self.label,
125 .stmts = try c.arena.dupe(Node, self.statements.items),
126 });
127 }
128
129 /// Given the desired name, return a name that does not shadow anything from outer scopes.
130 /// Inserts the returned name into the scope.
131 /// The name will not be visible to callers of getAlias.
132 fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
133 return scope.createMangledName(c, name, true);
134 }
135
136 /// Same as reserveMangledName, but enables the alias immediately.
137 fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
138 return scope.createMangledName(c, name, false);
139 }
140
141 fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
142 const name_copy = try c.arena.dupe(u8, name);
143 var proposed_name = name_copy;
144 while (scope.contains(proposed_name)) {
145 scope.mangle_count += 1;
146 proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
147 }
148 const new_mangle = try scope.variables.addOne();
149 if (reservation) {
150 new_mangle.* = .{ .name = name_copy, .alias = name_copy };
151 } else {
152 new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
153 }
154 return proposed_name;
155 }
156
157 fn getAlias(scope: *Block, name: []const u8) []const u8 {
158 for (scope.variables.items) |p| {
159 if (mem.eql(u8, p.name, name))
160 return p.alias;
161 }
162 return scope.base.parent.?.getAlias(name);
163 }
164
165 fn localContains(scope: *Block, name: []const u8) bool {
166 for (scope.variables.items) |p| {
167 if (mem.eql(u8, p.alias, name))
168 return true;
169 }
170 return false;
171 }
172
173 fn contains(scope: *Block, name: []const u8) bool {
174 if (scope.localContains(name))
175 return true;
176 return scope.base.parent.?.contains(name);
177 }
178
179 fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
180 const name_node = try Tag.identifier.create(c.arena, name);
181 const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
182 try scope.statements.append(discard);
183 try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
184 }
185 };
186
187 const Root = struct {
188 base: Scope,
189 sym_table: SymbolTable,
190 macro_table: SymbolTable,
191 context: *Context,
192 nodes: std.ArrayList(Node),
193
194 fn init(c: *Context) Root {
195 return .{
196 .base = .{
197 .id = .root,
198 .parent = null,
199 },
200 .sym_table = SymbolTable.init(c.gpa),
201 .macro_table = SymbolTable.init(c.gpa),
202 .context = c,
203 .nodes = std.ArrayList(Node).init(c.gpa),
204 };
205 }
206
207 fn deinit(scope: *Root) void {
208 scope.sym_table.deinit();
209 scope.macro_table.deinit();
210 scope.nodes.deinit();
211 }
212
213 /// Check if the global scope contains this name, without looking into the "future", e.g.
214 /// ignore the preprocessed decl and macro names.
215 fn containsNow(scope: *Root, name: []const u8) bool {
216 return scope.sym_table.contains(name) or scope.macro_table.contains(name);
217 }
218
219 /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
220 fn contains(scope: *Root, name: []const u8) bool {
221 return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name);
222 }
223 };
224
225 fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
226 var scope = inner;
227 while (true) {
228 switch (scope.id) {
229 .root => unreachable,
230 .block => return @fieldParentPtr(Block, "base", scope),
231 .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
232 else => scope = scope.parent.?,
233 }
234 }
235 }
236
237 fn findBlockReturnType(inner: *Scope) clang.QualType {
238 var scope = inner;
239 while (true) {
240 switch (scope.id) {
241 .root => unreachable,
242 .block => {
243 const block = @fieldParentPtr(Block, "base", scope);
244 if (block.return_type) |qt| return qt;
245 scope = scope.parent.?;
246 },
247 else => scope = scope.parent.?,
248 }
249 }
250 }
251
252 fn getAlias(scope: *Scope, name: []const u8) []const u8 {
253 return switch (scope.id) {
254 .root => return name,
255 .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
256 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
257 };
258 }
259
260 fn contains(scope: *Scope, name: []const u8) bool {
261 return switch (scope.id) {
262 .root => @fieldParentPtr(Root, "base", scope).contains(name),
263 .block => @fieldParentPtr(Block, "base", scope).contains(name),
264 .loop, .do_loop, .condition => scope.parent.?.contains(name),
265 };
266 }
267
268 fn getBreakableScope(inner: *Scope) *Scope {
269 var scope = inner;
270 while (true) {
271 switch (scope.id) {
272 .root => unreachable,
273 .loop, .do_loop => return scope,
274 else => scope = scope.parent.?,
275 }
276 }
277 }
278
279 /// Appends a node to the first block scope if inside a function, or to the root tree if not.
280 fn appendNode(inner: *Scope, node: Node) !void {
281 var scope = inner;
282 while (true) {
283 switch (scope.id) {
284 .root => {
285 const root = @fieldParentPtr(Root, "base", scope);
286 return root.nodes.append(node);
287 },
288 .block => {
289 const block = @fieldParentPtr(Block, "base", scope);
290 return block.statements.append(node);
291 },
292 else => scope = scope.parent.?,
293 }
294 }
295 }
296
297 fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
298 var scope = inner;
299 while (true) {
300 switch (scope.id) {
301 .root => return,
302 .block => {
303 const block = @fieldParentPtr(Block, "base", scope);
304 if (block.variable_discards.get(name)) |discard| {
305 discard.data.should_skip = true;
306 return;
307 }
308 },
309 else => {},
310 }
311 scope = scope.parent.?;
312 }
313 }
314};
315
31628pub const Context = struct {
31729 gpa: mem.Allocator,
31830 arena: mem.Allocator,
......@@ -829,7 +541,7 @@ fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType,
829541/// var static = S.*;
830542/// }).static;
831543fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node {
832 const var_name = Scope.Block.StaticInnerName;
544 const var_name = Scope.Block.static_inner_name;
833545
834546 const variables = try c.arena.alloc(Node, 1);
835547 variables[0] = try Tag.mut_str.create(c.arena, .{ .name = var_name, .init = str });
......@@ -1423,11 +1135,6 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E
14231135 }
14241136}
14251137
1426pub const ResultUsed = enum {
1427 used,
1428 unused,
1429};
1430
14311138fn transStmt(
14321139 c: *Context,
14331140 scope: *Scope,
......@@ -2070,7 +1777,7 @@ fn transDeclStmtOne(
20701777 init_node = try removeCVQualifiers(c, dst_type_node, init_node);
20711778 }
20721779
2073 const var_name: []const u8 = if (is_static_local) Scope.Block.StaticInnerName else mangled_name;
1780 const var_name: []const u8 = if (is_static_local) Scope.Block.static_inner_name else mangled_name;
20741781 var node = try Tag.var_decl.create(c.arena, .{
20751782 .is_pub = false,
20761783 .is_const = is_const,
......@@ -2153,7 +1860,7 @@ fn transDeclRefExpr(
21531860 if (var_decl.isStaticLocal()) {
21541861 ref_expr = try Tag.field_access.create(c.arena, .{
21551862 .lhs = ref_expr,
2156 .field_name = Scope.Block.StaticInnerName,
1863 .field_name = Scope.Block.static_inner_name,
21571864 });
21581865 }
21591866 }
src/translate_c/common.zig created+311
......@@ -0,0 +1,311 @@
1const std = @import("std");
2const ast = @import("ast.zig");
3const Node = ast.Node;
4const Tag = Node.Tag;
5
6const CallingConvention = std.builtin.CallingConvention;
7
8pub const Error = std.mem.Allocator.Error;
9pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
10pub const TypeError = Error || error{UnsupportedType};
11pub const TransError = TypeError || error{UnsupportedTranslation};
12
13pub const SymbolTable = std.StringArrayHashMap(Node);
14pub const AliasList = std.ArrayList(struct {
15 alias: []const u8,
16 name: []const u8,
17});
18
19pub const ResultUsed = enum {
20 used,
21 unused,
22};
23
24pub fn ScopeExtra(comptime Context: type, comptime Type: type) type {
25 return struct {
26 id: Id,
27 parent: ?*Scope,
28
29 const Scope = @This();
30
31 pub const Id = enum {
32 block,
33 root,
34 condition,
35 loop,
36 do_loop,
37 };
38
39 /// Used for the scope of condition expressions, for example `if (cond)`.
40 /// The block is lazily initialised because it is only needed for rare
41 /// cases of comma operators being used.
42 pub const Condition = struct {
43 base: Scope,
44 block: ?Block = null,
45
46 pub fn getBlockScope(self: *Condition, c: *Context) !*Block {
47 if (self.block) |*b| return b;
48 self.block = try Block.init(c, &self.base, true);
49 return &self.block.?;
50 }
51
52 pub fn deinit(self: *Condition) void {
53 if (self.block) |*b| b.deinit();
54 }
55 };
56
57 /// Represents an in-progress Node.Block. This struct is stack-allocated.
58 /// When it is deinitialized, it produces an Node.Block which is allocated
59 /// into the main arena.
60 pub const Block = struct {
61 base: Scope,
62 statements: std.ArrayList(Node),
63 variables: AliasList,
64 mangle_count: u32 = 0,
65 label: ?[]const u8 = null,
66
67 /// By default all variables are discarded, since we do not know in advance if they
68 /// will be used. This maps the variable's name to the Discard payload, so that if
69 /// the variable is subsequently referenced we can indicate that the discard should
70 /// be skipped during the intermediate AST -> Zig AST render step.
71 variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
72
73 /// When the block corresponds to a function, keep track of the return type
74 /// so that the return expression can be cast, if necessary
75 return_type: ?Type = null,
76
77 /// C static local variables are wrapped in a block-local struct. The struct
78 /// is named after the (mangled) variable name, the Zig variable within the
79 /// struct itself is given this name.
80 pub const static_inner_name = "static";
81
82 pub fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
83 var blk = Block{
84 .base = .{
85 .id = .block,
86 .parent = parent,
87 },
88 .statements = std.ArrayList(Node).init(c.gpa),
89 .variables = AliasList.init(c.gpa),
90 .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
91 };
92 if (labeled) {
93 blk.label = try blk.makeMangledName(c, "blk");
94 }
95 return blk;
96 }
97
98 pub fn deinit(self: *Block) void {
99 self.statements.deinit();
100 self.variables.deinit();
101 self.variable_discards.deinit();
102 self.* = undefined;
103 }
104
105 pub fn complete(self: *Block, c: *Context) !Node {
106 if (self.base.parent.?.id == .do_loop) {
107 // We reserve 1 extra statement if the parent is a do_loop. This is in case of
108 // do while, we want to put `if (cond) break;` at the end.
109 const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop);
110 var stmts = try c.arena.alloc(Node, alloc_len);
111 stmts.len = self.statements.items.len;
112 @memcpy(stmts[0..self.statements.items.len], self.statements.items);
113 return Tag.block.create(c.arena, .{
114 .label = self.label,
115 .stmts = stmts,
116 });
117 }
118 if (self.statements.items.len == 0) return Tag.empty_block.init();
119 return Tag.block.create(c.arena, .{
120 .label = self.label,
121 .stmts = try c.arena.dupe(Node, self.statements.items),
122 });
123 }
124
125 /// Given the desired name, return a name that does not shadow anything from outer scopes.
126 /// Inserts the returned name into the scope.
127 /// The name will not be visible to callers of getAlias.
128 pub fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
129 return scope.createMangledName(c, name, true);
130 }
131
132 /// Same as reserveMangledName, but enables the alias immediately.
133 pub fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
134 return scope.createMangledName(c, name, false);
135 }
136
137 pub fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
138 const name_copy = try c.arena.dupe(u8, name);
139 var proposed_name = name_copy;
140 while (scope.contains(proposed_name)) {
141 scope.mangle_count += 1;
142 proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
143 }
144 const new_mangle = try scope.variables.addOne();
145 if (reservation) {
146 new_mangle.* = .{ .name = name_copy, .alias = name_copy };
147 } else {
148 new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
149 }
150 return proposed_name;
151 }
152
153 pub fn getAlias(scope: *Block, name: []const u8) []const u8 {
154 for (scope.variables.items) |p| {
155 if (std.mem.eql(u8, p.name, name))
156 return p.alias;
157 }
158 return scope.base.parent.?.getAlias(name);
159 }
160
161 pub fn localContains(scope: *Block, name: []const u8) bool {
162 for (scope.variables.items) |p| {
163 if (std.mem.eql(u8, p.alias, name))
164 return true;
165 }
166 return false;
167 }
168
169 pub fn contains(scope: *Block, name: []const u8) bool {
170 if (scope.localContains(name))
171 return true;
172 return scope.base.parent.?.contains(name);
173 }
174
175 pub fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
176 const name_node = try Tag.identifier.create(c.arena, name);
177 const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
178 try scope.statements.append(discard);
179 try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
180 }
181 };
182
183 pub const Root = struct {
184 base: Scope,
185 sym_table: SymbolTable,
186 macro_table: SymbolTable,
187 context: *Context,
188 nodes: std.ArrayList(Node),
189
190 pub fn init(c: *Context) Root {
191 return .{
192 .base = .{
193 .id = .root,
194 .parent = null,
195 },
196 .sym_table = SymbolTable.init(c.gpa),
197 .macro_table = SymbolTable.init(c.gpa),
198 .context = c,
199 .nodes = std.ArrayList(Node).init(c.gpa),
200 };
201 }
202
203 pub fn deinit(scope: *Root) void {
204 scope.sym_table.deinit();
205 scope.macro_table.deinit();
206 scope.nodes.deinit();
207 }
208
209 /// Check if the global scope contains this name, without looking into the "future", e.g.
210 /// ignore the preprocessed decl and macro names.
211 pub fn containsNow(scope: *Root, name: []const u8) bool {
212 return scope.sym_table.contains(name) or scope.macro_table.contains(name);
213 }
214
215 /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
216 pub fn contains(scope: *Root, name: []const u8) bool {
217 return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name);
218 }
219 };
220
221 pub fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
222 var scope = inner;
223 while (true) {
224 switch (scope.id) {
225 .root => unreachable,
226 .block => return @fieldParentPtr(Block, "base", scope),
227 .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
228 else => scope = scope.parent.?,
229 }
230 }
231 }
232
233 pub fn findBlockReturnType(inner: *Scope) Type {
234 var scope = inner;
235 while (true) {
236 switch (scope.id) {
237 .root => unreachable,
238 .block => {
239 const block = @fieldParentPtr(Block, "base", scope);
240 if (block.return_type) |ty| return ty;
241 scope = scope.parent.?;
242 },
243 else => scope = scope.parent.?,
244 }
245 }
246 }
247
248 pub fn getAlias(scope: *Scope, name: []const u8) []const u8 {
249 return switch (scope.id) {
250 .root => return name,
251 .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
252 .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
253 };
254 }
255
256 pub fn contains(scope: *Scope, name: []const u8) bool {
257 return switch (scope.id) {
258 .root => @fieldParentPtr(Root, "base", scope).contains(name),
259 .block => @fieldParentPtr(Block, "base", scope).contains(name),
260 .loop, .do_loop, .condition => scope.parent.?.contains(name),
261 };
262 }
263
264 pub fn getBreakableScope(inner: *Scope) *Scope {
265 var scope = inner;
266 while (true) {
267 switch (scope.id) {
268 .root => unreachable,
269 .loop, .do_loop => return scope,
270 else => scope = scope.parent.?,
271 }
272 }
273 }
274
275 /// Appends a node to the first block scope if inside a function, or to the root tree if not.
276 pub fn appendNode(inner: *Scope, node: Node) !void {
277 var scope = inner;
278 while (true) {
279 switch (scope.id) {
280 .root => {
281 const root = @fieldParentPtr(Root, "base", scope);
282 return root.nodes.append(node);
283 },
284 .block => {
285 const block = @fieldParentPtr(Block, "base", scope);
286 return block.statements.append(node);
287 },
288 else => scope = scope.parent.?,
289 }
290 }
291 }
292
293 pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
294 var scope = inner;
295 while (true) {
296 switch (scope.id) {
297 .root => return,
298 .block => {
299 const block = @fieldParentPtr(Block, "base", scope);
300 if (block.variable_discards.get(name)) |discard| {
301 discard.data.should_skip = true;
302 return;
303 }
304 },
305 else => {},
306 }
307 scope = scope.parent.?;
308 }
309 }
310 };
311}