blob 2fbcf72c (261581B) - Raw
1 const std = @import("std"); 2 const testing = std.testing; 3 const assert = std.debug.assert; 4 const mem = std.mem; 5 const math = std.math; 6 const meta = std.meta; 7 const CallingConvention = std.builtin.CallingConvention; 8 const clang = @import("clang.zig"); 9 const aro = @import("aro"); 10 const CToken = aro.Tokenizer.Token; 11 const Node = ast.Node; 12 const Tag = Node.Tag; 13 const common = @import("aro_translate_c"); 14 const ast = common.ast; 15 const Error = common.Error; 16 const MacroProcessingError = common.MacroProcessingError; 17 const TypeError = common.TypeError; 18 const TransError = common.TransError; 19 const SymbolTable = common.SymbolTable; 20 const AliasList = common.AliasList; 21 const ResultUsed = common.ResultUsed; 22 const Scope = common.ScopeExtra(Context, clang.QualType); 23 const PatternList = common.PatternList; 24 const MacroSlicer = common.MacroSlicer; 25 26 pub const Context = struct { 27 gpa: mem.Allocator, 28 arena: mem.Allocator, 29 source_manager: *clang.SourceManager, 30 decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{}, 31 alias_list: AliasList, 32 global_scope: *Scope.Root, 33 clang_context: *clang.ASTContext, 34 mangle_count: u32 = 0, 35 /// Table of record decls that have been demoted to opaques. 36 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, 37 /// Table of unnamed enums and records that are child types of typedefs. 38 unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{}, 39 /// Needed to decide if we are parsing a typename 40 typedefs: std.StringArrayHashMapUnmanaged(void) = .{}, 41 42 /// This one is different than the root scope's name table. This contains 43 /// a list of names that we found by visiting all the top level decls without 44 /// translating them. The other maps are updated as we translate; this one is updated 45 /// up front in a pre-processing step. 46 global_names: std.StringArrayHashMapUnmanaged(void) = .{}, 47 48 /// This is similar to `global_names`, but contains names which we would 49 /// *like* to use, but do not strictly *have* to if they are unavailable. 50 /// These are relevant to types, which ideally we would name like 51 /// 'struct_foo' with an alias 'foo', but if either of those names is taken, 52 /// may be mangled. 53 /// This is distinct from `global_names` so we can detect at a type 54 /// declaration whether or not the name is available. 55 weak_global_names: std.StringArrayHashMapUnmanaged(void) = .{}, 56 57 pattern_list: PatternList, 58 59 fn getMangle(c: *Context) u32 { 60 c.mangle_count += 1; 61 return c.mangle_count; 62 } 63 64 /// Convert a null-terminated C string to a slice allocated in the arena 65 fn str(c: *Context, s: [*:0]const u8) ![]u8 { 66 return c.arena.dupe(u8, mem.sliceTo(s, 0)); 67 } 68 69 /// Convert a clang source location to a file:line:column string 70 fn locStr(c: *Context, loc: clang.SourceLocation) ![]u8 { 71 const spelling_loc = c.source_manager.getSpellingLoc(loc); 72 const filename_c = c.source_manager.getFilename(spelling_loc); 73 const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)"); 74 75 const line = c.source_manager.getSpellingLineNumber(spelling_loc); 76 const column = c.source_manager.getSpellingColumnNumber(spelling_loc); 77 return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); 78 } 79 }; 80 81 pub fn translate( 82 gpa: mem.Allocator, 83 args_begin: [*]?[*]const u8, 84 args_end: [*]?[*]const u8, 85 errors: *std.zig.ErrorBundle, 86 resources_path: [*:0]const u8, 87 ) !std.zig.Ast { 88 var clang_errors: []clang.ErrorMsg = &.{}; 89 90 const ast_unit = clang.LoadFromCommandLine( 91 args_begin, 92 args_end, 93 &clang_errors.ptr, 94 &clang_errors.len, 95 resources_path, 96 ) orelse { 97 defer clang.ErrorMsg.delete(clang_errors.ptr, clang_errors.len); 98 99 var bundle: std.zig.ErrorBundle.Wip = undefined; 100 try bundle.init(gpa); 101 defer bundle.deinit(); 102 103 for (clang_errors) |c_error| { 104 const line = line: { 105 const source = c_error.source orelse break :line 0; 106 var start = c_error.offset; 107 while (start > 0) : (start -= 1) { 108 if (source[start - 1] == '\n') break; 109 } 110 var end = c_error.offset; 111 while (true) : (end += 1) { 112 if (source[end] == 0) break; 113 if (source[end] == '\n') break; 114 } 115 break :line try bundle.addString(source[start..end]); 116 }; 117 118 try bundle.addRootErrorMessage(.{ 119 .msg = try bundle.addString(c_error.msg_ptr[0..c_error.msg_len]), 120 .src_loc = if (c_error.filename_ptr) |filename_ptr| try bundle.addSourceLocation(.{ 121 .src_path = try bundle.addString(filename_ptr[0..c_error.filename_len]), 122 .span_start = c_error.offset, 123 .span_main = c_error.offset, 124 .span_end = c_error.offset + 1, 125 .line = c_error.line, 126 .column = c_error.column, 127 .source_line = line, 128 }) else .none, 129 }); 130 } 131 errors.* = try bundle.toOwnedBundle(""); 132 133 return error.SemanticAnalyzeFail; 134 }; 135 defer ast_unit.delete(); 136 137 // For memory that has the same lifetime as the Ast that we return 138 // from this function. 139 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 140 defer arena_allocator.deinit(); 141 const arena = arena_allocator.allocator(); 142 143 var context = Context{ 144 .gpa = gpa, 145 .arena = arena, 146 .source_manager = ast_unit.getSourceManager(), 147 .alias_list = AliasList.init(gpa), 148 .global_scope = try arena.create(Scope.Root), 149 .clang_context = ast_unit.getASTContext(), 150 .pattern_list = try PatternList.init(gpa), 151 }; 152 context.global_scope.* = Scope.Root.init(&context); 153 defer { 154 context.decl_table.deinit(gpa); 155 context.alias_list.deinit(); 156 context.global_names.deinit(gpa); 157 context.opaque_demotes.deinit(gpa); 158 context.unnamed_typedefs.deinit(gpa); 159 context.typedefs.deinit(gpa); 160 context.global_scope.deinit(); 161 context.pattern_list.deinit(gpa); 162 } 163 164 inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| { 165 const builtin = try Tag.pub_var_simple.create(arena, .{ 166 .name = decl.name, 167 .init = try Tag.import_c_builtin.create(arena, decl.name), 168 }); 169 try addTopLevelDecl(&context, decl.name, builtin); 170 } 171 172 try prepopulateGlobalNameTable(ast_unit, &context); 173 174 if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) { 175 return error.OutOfMemory; 176 } 177 178 try transPreprocessorEntities(&context, ast_unit); 179 180 try addMacros(&context); 181 for (context.alias_list.items) |alias| { 182 const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name }); 183 try addTopLevelDecl(&context, alias.alias, node); 184 } 185 186 return ast.render(gpa, context.global_scope.nodes.items); 187 } 188 189 /// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens) 190 /// Macros of this form will not be translated. 191 fn isSelfDefinedMacro(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) !bool { 192 const source = try getMacroText(unit, c, macro); 193 var tokenizer: aro.Tokenizer = .{ 194 .buf = source, 195 .source = .unused, 196 .langopts = .{}, 197 }; 198 const name_tok = tokenizer.nextNoWS(); 199 const name = source[name_tok.start..name_tok.end]; 200 201 const first_tok = tokenizer.nextNoWS(); 202 // We do not just check for `.Identifier` below because keyword tokens are preferentially matched first by 203 // the tokenizer. 204 // In other words we would miss `#define inline inline` (`inline` is a valid c89 identifier) 205 if (first_tok.id == .eof) return false; 206 return mem.eql(u8, name, source[first_tok.start..first_tok.end]); 207 } 208 209 fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { 210 if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) { 211 return error.OutOfMemory; 212 } 213 214 // TODO if we see #undef, delete it from the table 215 var it = ast_unit.getLocalPreprocessingEntities_begin(); 216 const it_end = ast_unit.getLocalPreprocessingEntities_end(); 217 218 while (it.I != it_end.I) : (it.I += 1) { 219 const entity = it.deref(); 220 switch (entity.getKind()) { 221 .MacroDefinitionKind => { 222 const macro = @as(*clang.MacroDefinitionRecord, @ptrCast(entity)); 223 const raw_name = macro.getName_getNameStart(); 224 const name = try c.str(raw_name); 225 226 if (!try isSelfDefinedMacro(ast_unit, c, macro)) { 227 try c.global_names.put(c.gpa, name, {}); 228 } 229 }, 230 else => {}, 231 } 232 } 233 } 234 235 fn declVisitorNamesOnlyC(context: ?*anyopaque, decl: *const clang.Decl) callconv(.C) bool { 236 const c: *Context = @ptrCast(@alignCast(context)); 237 declVisitorNamesOnly(c, decl) catch return false; 238 return true; 239 } 240 241 fn declVisitorC(context: ?*anyopaque, decl: *const clang.Decl) callconv(.C) bool { 242 const c: *Context = @ptrCast(@alignCast(context)); 243 declVisitor(c, decl) catch return false; 244 return true; 245 } 246 247 fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { 248 if (decl.castToNamedDecl()) |named_decl| { 249 const decl_name = try c.str(named_decl.getName_bytes_begin()); 250 251 switch (decl.getKind()) { 252 .Record, .Enum => { 253 // These types are prefixed with the container kind. 254 const container_prefix = if (decl.getKind() == .Record) prefix: { 255 const record_decl: *const clang.RecordDecl = @ptrCast(decl); 256 if (record_decl.isUnion()) { 257 break :prefix "union"; 258 } else { 259 break :prefix "struct"; 260 } 261 } else "enum"; 262 const prefixed_name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_prefix, decl_name }); 263 // `decl_name` and `prefixed_name` are the preferred names for this type. 264 // However, we can name it anything else if necessary, so these are "weak names". 265 try c.weak_global_names.ensureUnusedCapacity(c.gpa, 2); 266 c.weak_global_names.putAssumeCapacity(decl_name, {}); 267 c.weak_global_names.putAssumeCapacity(prefixed_name, {}); 268 }, 269 else => { 270 try c.global_names.put(c.gpa, decl_name, {}); 271 }, 272 } 273 274 // Check for typedefs with unnamed enum/record child types. 275 if (decl.getKind() == .Typedef) { 276 const typedef_decl = @as(*const clang.TypedefNameDecl, @ptrCast(decl)); 277 var child_ty = typedef_decl.getUnderlyingType().getTypePtr(); 278 const addr: usize = while (true) switch (child_ty.getTypeClass()) { 279 .Enum => { 280 const enum_ty = @as(*const clang.EnumType, @ptrCast(child_ty)); 281 const enum_decl = enum_ty.getDecl(); 282 // check if this decl is unnamed 283 if (@as(*const clang.NamedDecl, @ptrCast(enum_decl)).getName_bytes_begin()[0] != 0) return; 284 break @intFromPtr(enum_decl.getCanonicalDecl()); 285 }, 286 .Record => { 287 const record_ty = @as(*const clang.RecordType, @ptrCast(child_ty)); 288 const record_decl = record_ty.getDecl(); 289 // check if this decl is unnamed 290 if (@as(*const clang.NamedDecl, @ptrCast(record_decl)).getName_bytes_begin()[0] != 0) return; 291 break @intFromPtr(record_decl.getCanonicalDecl()); 292 }, 293 .Elaborated => { 294 const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(child_ty)); 295 child_ty = elaborated_ty.getNamedType().getTypePtr(); 296 }, 297 .Decayed => { 298 const decayed_ty = @as(*const clang.DecayedType, @ptrCast(child_ty)); 299 child_ty = decayed_ty.getDecayedType().getTypePtr(); 300 }, 301 .Attributed => { 302 const attributed_ty = @as(*const clang.AttributedType, @ptrCast(child_ty)); 303 child_ty = attributed_ty.getEquivalentType().getTypePtr(); 304 }, 305 .MacroQualified => { 306 const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(child_ty)); 307 child_ty = macroqualified_ty.getModifiedType().getTypePtr(); 308 }, 309 else => return, 310 }; 311 312 const result = try c.unnamed_typedefs.getOrPut(c.gpa, addr); 313 if (result.found_existing) { 314 // One typedef can declare multiple names. 315 // Don't put this one in `decl_table` so it's processed later. 316 return; 317 } 318 result.value_ptr.* = decl_name; 319 // Put this typedef in the decl_table to avoid redefinitions. 320 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(typedef_decl.getCanonicalDecl()), decl_name); 321 try c.typedefs.put(c.gpa, decl_name, {}); 322 } 323 } 324 } 325 326 fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { 327 switch (decl.getKind()) { 328 .Function => { 329 return visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl))); 330 }, 331 .Typedef => { 332 try transTypeDef(c, &c.global_scope.base, @as(*const clang.TypedefNameDecl, @ptrCast(decl))); 333 }, 334 .Enum => { 335 try transEnumDecl(c, &c.global_scope.base, @as(*const clang.EnumDecl, @ptrCast(decl))); 336 }, 337 .Record => { 338 try transRecordDecl(c, &c.global_scope.base, @as(*const clang.RecordDecl, @ptrCast(decl))); 339 }, 340 .Var => { 341 return visitVarDecl(c, @as(*const clang.VarDecl, @ptrCast(decl)), null); 342 }, 343 .Empty => { 344 // Do nothing 345 }, 346 .FileScopeAsm => { 347 try transFileScopeAsm(c, &c.global_scope.base, @as(*const clang.FileScopeAsmDecl, @ptrCast(decl))); 348 }, 349 else => { 350 const decl_name = try c.str(decl.getDeclKindName()); 351 try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); 352 }, 353 } 354 } 355 356 fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.FileScopeAsmDecl) Error!void { 357 const asm_string = file_scope_asm.getAsmString(); 358 var len: usize = undefined; 359 const bytes_ptr = asm_string.getString_bytes_begin_size(&len); 360 361 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); 362 const str_node = try Tag.string_literal.create(c.arena, str); 363 364 const asm_node = try Tag.asm_simple.create(c.arena, str_node); 365 const block = try Tag.block_single.create(c.arena, asm_node); 366 const comptime_node = try Tag.@"comptime".create(c.arena, block); 367 368 try scope.appendNode(comptime_node); 369 } 370 371 fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { 372 const fn_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(fn_decl)).getName_bytes_begin()); 373 if (c.global_scope.sym_table.contains(fn_name)) 374 return; // Avoid processing this decl twice 375 376 // Skip this declaration if a proper definition exists 377 if (!fn_decl.isThisDeclarationADefinition()) { 378 if (fn_decl.getDefinition()) |def| 379 return visitFnDecl(c, def); 380 } 381 382 const fn_decl_loc = fn_decl.getLocation(); 383 const has_body = fn_decl.hasBody(); 384 const storage_class = fn_decl.getStorageClass(); 385 const is_always_inline = has_body and fn_decl.hasAlwaysInlineAttr(); 386 var decl_ctx = FnDeclContext{ 387 .fn_name = fn_name, 388 .has_body = has_body, 389 .storage_class = storage_class, 390 .is_always_inline = is_always_inline, 391 .is_export = switch (storage_class) { 392 .None => has_body and !is_always_inline and !fn_decl.isInlineSpecified(), 393 .Extern, .Static => false, 394 .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}), 395 .Auto => unreachable, // Not legal on functions 396 .Register => unreachable, // Not legal on functions 397 }, 398 }; 399 400 var fn_qt = fn_decl.getType(); 401 402 const fn_type = while (true) { 403 const fn_type = fn_qt.getTypePtr(); 404 405 switch (fn_type.getTypeClass()) { 406 .Attributed => { 407 const attr_type = @as(*const clang.AttributedType, @ptrCast(fn_type)); 408 fn_qt = attr_type.getEquivalentType(); 409 }, 410 .Paren => { 411 const paren_type = @as(*const clang.ParenType, @ptrCast(fn_type)); 412 fn_qt = paren_type.getInnerType(); 413 }, 414 else => break fn_type, 415 } 416 }; 417 const fn_ty = @as(*const clang.FunctionType, @ptrCast(fn_type)); 418 const return_qt = fn_ty.getReturnType(); 419 420 const proto_node = switch (fn_type.getTypeClass()) { 421 .FunctionProto => blk: { 422 const fn_proto_type = @as(*const clang.FunctionProtoType, @ptrCast(fn_type)); 423 if (has_body and fn_proto_type.isVariadic()) { 424 decl_ctx.has_body = false; 425 decl_ctx.storage_class = .Extern; 426 decl_ctx.is_export = false; 427 decl_ctx.is_always_inline = false; 428 try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to extern", .{}); 429 } 430 break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { 431 error.UnsupportedType => { 432 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); 433 }, 434 error.OutOfMemory => |e| return e, 435 }; 436 }, 437 .FunctionNoProto => blk: { 438 const fn_no_proto_type = @as(*const clang.FunctionType, @ptrCast(fn_type)); 439 break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { 440 error.UnsupportedType => { 441 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); 442 }, 443 error.OutOfMemory => |e| return e, 444 }; 445 }, 446 else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{fn_type.getTypeClass()}), 447 }; 448 449 if (!decl_ctx.has_body) { 450 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); 451 } 452 453 // actual function definition with body 454 const body_stmt = fn_decl.getBody(); 455 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false); 456 block_scope.return_type = return_qt; 457 defer block_scope.deinit(); 458 459 const scope = &block_scope.base; 460 461 var param_id: c_uint = 0; 462 for (proto_node.data.params) |*param| { 463 const param_name = param.name orelse { 464 proto_node.data.is_extern = true; 465 proto_node.data.is_export = false; 466 proto_node.data.is_inline = false; 467 try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name}); 468 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); 469 }; 470 471 const c_param = fn_decl.getParamDecl(param_id); 472 const qual_type = c_param.getOriginalType(); 473 const is_const = qual_type.isConstQualified(); 474 475 const mangled_param_name = try block_scope.makeMangledName(c, param_name); 476 param.name = mangled_param_name; 477 478 if (!is_const) { 479 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); 480 const arg_name = try block_scope.makeMangledName(c, bare_arg_name); 481 param.name = arg_name; 482 483 const redecl_node = try Tag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); 484 try block_scope.statements.append(redecl_node); 485 } 486 try block_scope.discardVariable(c, mangled_param_name); 487 488 param_id += 1; 489 } 490 491 const casted_body = @as(*const clang.CompoundStmt, @ptrCast(body_stmt)); 492 transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) { 493 error.OutOfMemory => |e| return e, 494 error.UnsupportedTranslation, 495 error.UnsupportedType, 496 => { 497 proto_node.data.is_extern = true; 498 proto_node.data.is_export = false; 499 proto_node.data.is_inline = false; 500 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{}); 501 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); 502 }, 503 }; 504 // add return statement if the function didn't have one 505 blk: { 506 const maybe_body = try block_scope.complete(c); 507 if (fn_ty.getNoReturnAttr() or isAnyopaque(return_qt) or maybe_body.isNoreturn(false)) { 508 proto_node.data.body = maybe_body; 509 break :blk; 510 } 511 512 const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { 513 error.OutOfMemory => |e| return e, 514 error.UnsupportedTranslation, 515 error.UnsupportedType, 516 => { 517 proto_node.data.is_extern = true; 518 proto_node.data.is_export = false; 519 proto_node.data.is_inline = false; 520 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{}); 521 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); 522 }, 523 }; 524 const ret = try Tag.@"return".create(c.arena, rhs); 525 try block_scope.statements.append(ret); 526 proto_node.data.body = try block_scope.complete(c); 527 } 528 529 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); 530 } 531 532 fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node { 533 return if (decl_init) |init_expr| 534 transQualTypeInitialized(c, scope, qt, init_expr, loc) 535 else 536 transQualType(c, scope, qt, loc); 537 } 538 539 /// This is used in global scope to convert a string literal `S` to [*c]u8: 540 /// &(struct { 541 /// var static = S.*; 542 /// }).static; 543 fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node { 544 const var_name = Scope.Block.static_inner_name; 545 546 const variables = try c.arena.alloc(Node, 1); 547 variables[0] = try Tag.mut_str.create(c.arena, .{ .name = var_name, .init = str }); 548 549 const anon_struct = try Tag.@"struct".create(c.arena, .{ 550 .layout = .none, 551 .fields = &.{}, 552 .functions = &.{}, 553 .variables = variables, 554 }); 555 556 const member_access = try Tag.field_access.create(c.arena, .{ 557 .lhs = anon_struct, 558 .field_name = var_name, 559 }); 560 return Tag.address_of.create(c.arena, member_access); 561 } 562 563 /// if mangled_name is not null, this var decl was declared in a block scope. 564 fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void { 565 const var_name = mangled_name orelse try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin()); 566 if (c.global_scope.sym_table.contains(var_name)) 567 return; // Avoid processing this decl twice 568 569 const is_pub = mangled_name == null; 570 const is_threadlocal = var_decl.getTLSKind() != .None; 571 const scope = &c.global_scope.base; 572 const var_decl_loc = var_decl.getLocation(); 573 574 const qual_type = var_decl.getTypeSourceInfo_getType(); 575 const storage_class = var_decl.getStorageClass(); 576 const has_init = var_decl.hasInit(); 577 const decl_init = var_decl.getInit(); 578 var is_const = qual_type.isConstQualified(); 579 580 // In C extern variables with initializers behave like Zig exports. 581 // extern int foo = 2; 582 // does the same as: 583 // extern int foo; 584 // int foo = 2; 585 var is_extern = storage_class == .Extern and !has_init; 586 var is_export = !is_extern and storage_class != .Static; 587 588 if (!is_extern and qualTypeWasDemotedToOpaque(c, qual_type)) { 589 return failDecl(c, var_decl_loc, var_name, "non-extern variable has opaque type", .{}); 590 } 591 592 const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { 593 error.UnsupportedTranslation, error.UnsupportedType => { 594 return failDecl(c, var_decl_loc, var_name, "unable to resolve variable type", .{}); 595 }, 596 error.OutOfMemory => |e| return e, 597 }; 598 599 var init_node: ?Node = null; 600 601 // If the initialization expression is not present, initialize with undefined. 602 // If it is an integer literal, we can skip the @as since it will be redundant 603 // with the variable type. 604 if (has_init) trans_init: { 605 if (decl_init) |expr| { 606 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) 607 transStringLiteralInitializer(c, @as(*const clang.StringLiteral, @ptrCast(expr)), type_node) 608 else 609 transExprCoercing(c, scope, expr, .used); 610 init_node = node_or_error catch |err| switch (err) { 611 error.UnsupportedTranslation, 612 error.UnsupportedType, 613 => { 614 is_extern = true; 615 is_export = false; 616 try warn(c, scope, var_decl_loc, "unable to translate variable initializer, demoted to extern", .{}); 617 break :trans_init; 618 }, 619 error.OutOfMemory => |e| return e, 620 }; 621 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) { 622 init_node = try Tag.int_from_bool.create(c.arena, init_node.?); 623 } else if (init_node.?.tag() == .string_literal and qualTypeIsCharStar(qual_type)) { 624 init_node = try stringLiteralToCharStar(c, init_node.?); 625 } 626 } else { 627 init_node = Tag.undefined_literal.init(); 628 } 629 } else if (storage_class != .Extern) { 630 // The C language specification states that variables with static or threadlocal 631 // storage without an initializer are initialized to a zero value. 632 633 // std.mem.zeroes(T) 634 init_node = try Tag.std_mem_zeroes.create(c.arena, type_node); 635 } else if (qual_type.getTypeClass() == .IncompleteArray) { 636 // Oh no, an extern array of unknown size! These are really fun because there's no 637 // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer 638 // to the data initialized via @extern. 639 640 const name_str = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{var_name}); 641 init_node = try Tag.builtin_extern.create(c.arena, .{ 642 .type = type_node, 643 .name = try Tag.string_literal.create(c.arena, name_str), 644 }); 645 646 // Since this is really a pointer to the underlying data, we tweak a few properties. 647 is_extern = false; 648 is_const = true; 649 } 650 651 const linksection_string = blk: { 652 var str_len: usize = undefined; 653 if (var_decl.getSectionAttribute(&str_len)) |str_ptr| { 654 break :blk str_ptr[0..str_len]; 655 } 656 break :blk null; 657 }; 658 659 const node = try Tag.var_decl.create(c.arena, .{ 660 .is_pub = is_pub, 661 .is_const = is_const, 662 .is_extern = is_extern, 663 .is_export = is_export, 664 .is_threadlocal = is_threadlocal, 665 .linksection_string = linksection_string, 666 .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(), 667 .name = var_name, 668 .type = type_node, 669 .init = init_node, 670 }); 671 return addTopLevelDecl(c, var_name, node); 672 } 673 674 const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{ 675 .{ "uint8_t", "u8" }, 676 .{ "int8_t", "i8" }, 677 .{ "uint16_t", "u16" }, 678 .{ "int16_t", "i16" }, 679 .{ "uint32_t", "u32" }, 680 .{ "int32_t", "i32" }, 681 .{ "uint64_t", "u64" }, 682 .{ "int64_t", "i64" }, 683 .{ "intptr_t", "isize" }, 684 .{ "uintptr_t", "usize" }, 685 .{ "ssize_t", "isize" }, 686 .{ "size_t", "usize" }, 687 }); 688 689 fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNameDecl) Error!void { 690 if (c.decl_table.get(@intFromPtr(typedef_decl.getCanonicalDecl()))) |_| 691 return; // Avoid processing this decl twice 692 const toplevel = scope.id == .root; 693 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; 694 695 var name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(typedef_decl)).getName_bytes_begin()); 696 try c.typedefs.put(c.gpa, name, {}); 697 698 if (builtin_typedef_map.get(name)) |builtin| { 699 return c.decl_table.putNoClobber(c.gpa, @intFromPtr(typedef_decl.getCanonicalDecl()), builtin); 700 } 701 if (!toplevel) name = try bs.makeMangledName(c, name); 702 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(typedef_decl.getCanonicalDecl()), name); 703 704 const child_qt = typedef_decl.getUnderlyingType(); 705 const typedef_loc = typedef_decl.getLocation(); 706 const init_node = transQualType(c, scope, child_qt, typedef_loc) catch |err| switch (err) { 707 error.UnsupportedType => { 708 return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{}); 709 }, 710 error.OutOfMemory => |e| return e, 711 }; 712 713 const payload = try c.arena.create(ast.Payload.SimpleVarDecl); 714 payload.* = .{ 715 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@intFromBool(toplevel)] }, 716 .data = .{ 717 .name = name, 718 .init = init_node, 719 }, 720 }; 721 const node = Node.initPayload(&payload.base); 722 723 if (toplevel) { 724 try addTopLevelDecl(c, name, node); 725 } else { 726 try scope.appendNode(node); 727 if (node.tag() != .pub_var_simple) { 728 try bs.discardVariable(c, name); 729 } 730 } 731 } 732 733 /// Build a getter function for a flexible array member at the end of a C struct 734 /// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer 735 /// to the flexible array with the correct const and volatile qualifiers 736 fn buildFlexibleArrayFn( 737 c: *Context, 738 scope: *Scope, 739 layout: *const clang.ASTRecordLayout, 740 field_name: []const u8, 741 field_decl: *const clang.FieldDecl, 742 ) TypeError!Node { 743 const field_qt = field_decl.getType(); 744 const field_qt_canon = qualTypeCanon(field_qt); 745 746 const u8_type = try Tag.type.create(c.arena, "u8"); 747 const self_param_name = "self"; 748 const self_param = try Tag.identifier.create(c.arena, self_param_name); 749 const self_type = try Tag.typeof.create(c.arena, self_param); 750 751 const fn_params = try c.arena.alloc(ast.Payload.Param, 1); 752 753 fn_params[0] = .{ 754 .name = self_param_name, 755 .type = Tag.@"anytype".init(), 756 .is_noalias = false, 757 }; 758 759 const array_type = @as(*const clang.ArrayType, @ptrCast(field_qt_canon)); 760 const element_qt = array_type.getElementType(); 761 const element_type = try transQualType(c, scope, element_qt, field_decl.getLocation()); 762 763 var block_scope = try Scope.Block.init(c, scope, false); 764 defer block_scope.deinit(); 765 766 const intermediate_type_name = try block_scope.makeMangledName(c, "Intermediate"); 767 const intermediate_type = try Tag.helpers_flexible_array_type.create(c.arena, .{ .lhs = self_type, .rhs = u8_type }); 768 const intermediate_type_decl = try Tag.var_simple.create(c.arena, .{ 769 .name = intermediate_type_name, 770 .init = intermediate_type, 771 }); 772 try block_scope.statements.append(intermediate_type_decl); 773 const intermediate_type_ident = try Tag.identifier.create(c.arena, intermediate_type_name); 774 775 const return_type_name = try block_scope.makeMangledName(c, "ReturnType"); 776 const return_type = try Tag.helpers_flexible_array_type.create(c.arena, .{ .lhs = self_type, .rhs = element_type }); 777 const return_type_decl = try Tag.var_simple.create(c.arena, .{ 778 .name = return_type_name, 779 .init = return_type, 780 }); 781 try block_scope.statements.append(return_type_decl); 782 const return_type_ident = try Tag.identifier.create(c.arena, return_type_name); 783 784 const field_index = field_decl.getFieldIndex(); 785 const bit_offset = layout.getFieldOffset(field_index); // this is a target-specific constant based on the struct layout 786 const byte_offset = bit_offset / 8; 787 788 const casted_self = try Tag.as.create(c.arena, .{ 789 .lhs = intermediate_type_ident, 790 .rhs = try Tag.ptr_cast.create(c.arena, self_param), 791 }); 792 const field_offset = try transCreateNodeNumber(c, byte_offset, .int); 793 const field_ptr = try Tag.add.create(c.arena, .{ .lhs = casted_self, .rhs = field_offset }); 794 795 const ptr_cast = try Tag.as.create(c.arena, .{ 796 .lhs = return_type_ident, 797 .rhs = try Tag.ptr_cast.create( 798 c.arena, 799 try Tag.align_cast.create( 800 c.arena, 801 field_ptr, 802 ), 803 ), 804 }); 805 const return_stmt = try Tag.@"return".create(c.arena, ptr_cast); 806 try block_scope.statements.append(return_stmt); 807 808 const payload = try c.arena.create(ast.Payload.Func); 809 payload.* = .{ 810 .base = .{ .tag = .func }, 811 .data = .{ 812 .is_pub = true, 813 .is_extern = false, 814 .is_export = false, 815 .is_inline = false, 816 .is_var_args = false, 817 .name = field_name, 818 .linksection_string = null, 819 .explicit_callconv = null, 820 .params = fn_params, 821 .return_type = return_type, 822 .body = try block_scope.complete(c), 823 .alignment = null, 824 }, 825 }; 826 return Node.initPayload(&payload.base); 827 } 828 829 /// Return true if `field_decl` is the flexible array field for its parent record 830 fn isFlexibleArrayFieldDecl(c: *Context, field_decl: *const clang.FieldDecl) bool { 831 const record_decl = field_decl.getParent() orelse return false; 832 const record_flexible_field = flexibleArrayField(c, record_decl) orelse return false; 833 return field_decl == record_flexible_field; 834 } 835 836 /// Find the flexible array field for a record if any. A flexible array field is an 837 /// incomplete or zero-length array that occurs as the last field of a record. 838 /// clang's RecordDecl::hasFlexibleArrayMember is not suitable for determining 839 /// this because it returns false for a record that ends with a zero-length 840 /// array, but we consider those to be flexible arrays 841 fn flexibleArrayField(c: *Context, record_def: *const clang.RecordDecl) ?*const clang.FieldDecl { 842 var it = record_def.field_begin(); 843 const end_it = record_def.field_end(); 844 var flexible_field: ?*const clang.FieldDecl = null; 845 while (it.neq(end_it)) : (it = it.next()) { 846 const field_decl = it.deref(); 847 const ty = qualTypeCanon(field_decl.getType()); 848 const incomplete_or_zero_size = ty.isIncompleteOrZeroLengthArrayType(c.clang_context); 849 if (incomplete_or_zero_size) { 850 flexible_field = field_decl; 851 } else { 852 flexible_field = null; 853 } 854 } 855 return flexible_field; 856 } 857 858 fn mangleWeakGlobalName(c: *Context, want_name: []const u8) ![]const u8 { 859 var cur_name = want_name; 860 861 if (!c.weak_global_names.contains(want_name)) { 862 // This type wasn't noticed by the name detection pass, so nothing has been treating this as 863 // a weak global name. We must mangle it to avoid conflicts with locals. 864 cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() }); 865 } 866 867 while (c.global_names.contains(cur_name)) { 868 cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() }); 869 } 870 return cur_name; 871 } 872 873 fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void { 874 if (c.decl_table.get(@intFromPtr(record_decl.getCanonicalDecl()))) |_| 875 return; // Avoid processing this decl twice 876 const record_loc = record_decl.getLocation(); 877 const toplevel = scope.id == .root; 878 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; 879 880 var is_union = false; 881 var container_kind_name: []const u8 = undefined; 882 var bare_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(record_decl)).getName_bytes_begin()); 883 884 if (record_decl.isUnion()) { 885 container_kind_name = "union"; 886 is_union = true; 887 } else if (record_decl.isStruct()) { 888 container_kind_name = "struct"; 889 } else { 890 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), bare_name); 891 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); 892 } 893 894 var is_unnamed = false; 895 var name = bare_name; 896 if (c.unnamed_typedefs.get(@intFromPtr(record_decl.getCanonicalDecl()))) |typedef_name| { 897 bare_name = typedef_name; 898 name = typedef_name; 899 } else { 900 // Record declarations such as `struct {...} x` have no name but they're not 901 // anonymous hence here isAnonymousStructOrUnion is not needed 902 if (bare_name.len == 0) { 903 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); 904 is_unnamed = true; 905 } 906 name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); 907 if (toplevel and !is_unnamed) { 908 name = try mangleWeakGlobalName(c, name); 909 } 910 } 911 if (!toplevel) name = try bs.makeMangledName(c, name); 912 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), name); 913 914 const is_pub = toplevel and !is_unnamed; 915 const init_node = blk: { 916 const record_def = record_decl.getDefinition() orelse { 917 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {}); 918 break :blk Tag.opaque_literal.init(); 919 }; 920 921 var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); 922 defer fields.deinit(); 923 924 var functions = std.ArrayList(Node).init(c.gpa); 925 defer functions.deinit(); 926 927 const flexible_field = flexibleArrayField(c, record_def); 928 var unnamed_field_count: u32 = 0; 929 var it = record_def.field_begin(); 930 const end_it = record_def.field_end(); 931 const layout = record_def.getASTRecordLayout(c.clang_context); 932 const record_alignment = layout.getAlignment(); 933 934 while (it.neq(end_it)) : (it = it.next()) { 935 const field_decl = it.deref(); 936 const field_loc = field_decl.getLocation(); 937 const field_qt = field_decl.getType(); 938 939 if (field_decl.isBitField()) { 940 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {}); 941 try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); 942 break :blk Tag.opaque_literal.init(); 943 } 944 945 var is_anon = false; 946 var field_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin()); 947 if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { 948 // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. 949 field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); 950 unnamed_field_count += 1; 951 is_anon = true; 952 } 953 if (flexible_field == field_decl) { 954 const flexible_array_fn = buildFlexibleArrayFn(c, scope, layout, field_name, field_decl) catch |err| switch (err) { 955 error.UnsupportedType => { 956 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {}); 957 try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of flexible array field {s}", .{ container_kind_name, field_name }); 958 break :blk Tag.opaque_literal.init(); 959 }, 960 else => |e| return e, 961 }; 962 try functions.append(flexible_array_fn); 963 continue; 964 } 965 const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) { 966 error.UnsupportedType => { 967 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {}); 968 try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name }); 969 break :blk Tag.opaque_literal.init(); 970 }, 971 else => |e| return e, 972 }; 973 974 const alignment = if (flexible_field != null and field_decl.getFieldIndex() == 0) 975 @as(c_uint, @intCast(record_alignment)) 976 else 977 ClangAlignment.forField(c, field_decl, record_def).zigAlignment(); 978 979 // C99 introduced designated initializers for structs. Omitted fields are implicitly 980 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero 981 // values for translated struct fields permits Zig code to comfortably use such an API. 982 const default_value = if (record_decl.isStruct()) 983 try Tag.std_mem_zeroes.create(c.arena, field_type) 984 else 985 null; 986 987 if (is_anon) { 988 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(field_decl.getCanonicalDecl()), field_name); 989 } 990 991 try fields.append(.{ 992 .name = field_name, 993 .type = field_type, 994 .alignment = alignment, 995 .default_value = default_value, 996 }); 997 } 998 999 const record_payload = try c.arena.create(ast.Payload.Record); 1000 record_payload.* = .{ 1001 .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@intFromBool(is_union)] }, 1002 .data = .{ 1003 .layout = .@"extern", 1004 .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), 1005 .functions = try c.arena.dupe(Node, functions.items), 1006 .variables = &.{}, 1007 }, 1008 }; 1009 break :blk Node.initPayload(&record_payload.base); 1010 }; 1011 1012 const payload = try c.arena.create(ast.Payload.SimpleVarDecl); 1013 payload.* = .{ 1014 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] }, 1015 .data = .{ 1016 .name = name, 1017 .init = init_node, 1018 }, 1019 }; 1020 const node = Node.initPayload(&payload.base); 1021 if (toplevel) { 1022 try addTopLevelDecl(c, name, node); 1023 // Only add the alias if the name is available *and* it was caught by 1024 // name detection. Don't bother performing a weak mangle, since a 1025 // mangled name is of no real use here. 1026 if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name)) 1027 try c.alias_list.append(.{ .alias = bare_name, .name = name }); 1028 } else { 1029 try scope.appendNode(node); 1030 if (node.tag() != .pub_var_simple) { 1031 try bs.discardVariable(c, name); 1032 } 1033 } 1034 } 1035 1036 fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) Error!void { 1037 if (c.decl_table.get(@intFromPtr(enum_decl.getCanonicalDecl()))) |_| 1038 return; // Avoid processing this decl twice 1039 const enum_loc = enum_decl.getLocation(); 1040 const toplevel = scope.id == .root; 1041 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; 1042 1043 var is_unnamed = false; 1044 var bare_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(enum_decl)).getName_bytes_begin()); 1045 var name = bare_name; 1046 if (c.unnamed_typedefs.get(@intFromPtr(enum_decl.getCanonicalDecl()))) |typedef_name| { 1047 bare_name = typedef_name; 1048 name = typedef_name; 1049 } else { 1050 if (bare_name.len == 0) { 1051 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); 1052 is_unnamed = true; 1053 } 1054 name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); 1055 if (toplevel and !is_unnamed) { 1056 name = try mangleWeakGlobalName(c, name); 1057 } 1058 } 1059 if (!toplevel) name = try bs.makeMangledName(c, name); 1060 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(enum_decl.getCanonicalDecl()), name); 1061 1062 const enum_type_node = if (enum_decl.getDefinition()) |enum_def| blk: { 1063 var it = enum_def.enumerator_begin(); 1064 const end_it = enum_def.enumerator_end(); 1065 while (it.neq(end_it)) : (it = it.next()) { 1066 const enum_const = it.deref(); 1067 var enum_val_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(enum_const)).getName_bytes_begin()); 1068 if (!toplevel) { 1069 enum_val_name = try bs.makeMangledName(c, enum_val_name); 1070 } 1071 1072 const enum_const_qt = @as(*const clang.ValueDecl, @ptrCast(enum_const)).getType(); 1073 const enum_const_loc = @as(*const clang.Decl, @ptrCast(enum_const)).getLocation(); 1074 const enum_const_type_node: ?Node = transQualType(c, scope, enum_const_qt, enum_const_loc) catch |err| switch (err) { 1075 error.UnsupportedType => null, 1076 else => |e| return e, 1077 }; 1078 1079 const enum_const_def = try Tag.enum_constant.create(c.arena, .{ 1080 .name = enum_val_name, 1081 .is_public = toplevel, 1082 .type = enum_const_type_node, 1083 // TODO: as of LLVM 18, the return value from `enum_const.getInitVal` here needs 1084 // to be freed with a call to its free() method. 1085 .value = try transCreateNodeAPInt(c, enum_const.getInitVal()), 1086 }); 1087 if (toplevel) 1088 try addTopLevelDecl(c, enum_val_name, enum_const_def) 1089 else { 1090 try scope.appendNode(enum_const_def); 1091 try bs.discardVariable(c, enum_val_name); 1092 } 1093 } 1094 1095 const int_type = enum_decl.getIntegerType(); 1096 // The underlying type may be null in case of forward-declared enum 1097 // types, while that's not ISO-C compliant many compilers allow this and 1098 // default to the usual integer type used for all the enums. 1099 1100 // default to c_int since msvc and gcc default to different types 1101 break :blk if (int_type.ptr != null) 1102 transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) { 1103 error.UnsupportedType => { 1104 return failDecl(c, enum_loc, name, "unable to translate enum integer type", .{}); 1105 }, 1106 else => |e| return e, 1107 } 1108 else 1109 try Tag.type.create(c.arena, "c_int"); 1110 } else blk: { 1111 try c.opaque_demotes.put(c.gpa, @intFromPtr(enum_decl.getCanonicalDecl()), {}); 1112 break :blk Tag.opaque_literal.init(); 1113 }; 1114 1115 const is_pub = toplevel and !is_unnamed; 1116 const payload = try c.arena.create(ast.Payload.SimpleVarDecl); 1117 payload.* = .{ 1118 .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] }, 1119 .data = .{ 1120 .init = enum_type_node, 1121 .name = name, 1122 }, 1123 }; 1124 const node = Node.initPayload(&payload.base); 1125 if (toplevel) { 1126 try addTopLevelDecl(c, name, node); 1127 // Only add the alias if the name is available *and* it was caught by 1128 // name detection. Don't bother performing a weak mangle, since a 1129 // mangled name is of no real use here. 1130 if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name)) 1131 try c.alias_list.append(.{ .alias = bare_name, .name = name }); 1132 } else { 1133 try scope.appendNode(node); 1134 if (node.tag() != .pub_var_simple) { 1135 try bs.discardVariable(c, name); 1136 } 1137 } 1138 } 1139 1140 fn transStmt( 1141 c: *Context, 1142 scope: *Scope, 1143 stmt: *const clang.Stmt, 1144 result_used: ResultUsed, 1145 ) TransError!Node { 1146 const sc = stmt.getStmtClass(); 1147 switch (sc) { 1148 .BinaryOperatorClass => return transBinaryOperator(c, scope, @as(*const clang.BinaryOperator, @ptrCast(stmt)), result_used), 1149 .CompoundStmtClass => return transCompoundStmt(c, scope, @as(*const clang.CompoundStmt, @ptrCast(stmt))), 1150 .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @as(*const clang.CStyleCastExpr, @ptrCast(stmt)), result_used), 1151 .DeclStmtClass => return transDeclStmt(c, scope, @as(*const clang.DeclStmt, @ptrCast(stmt))), 1152 .DeclRefExprClass => return transDeclRefExpr(c, scope, @as(*const clang.DeclRefExpr, @ptrCast(stmt))), 1153 .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @as(*const clang.ImplicitCastExpr, @ptrCast(stmt)), result_used), 1154 .IntegerLiteralClass => return transIntegerLiteral(c, scope, @as(*const clang.IntegerLiteral, @ptrCast(stmt)), result_used, .with_as), 1155 .ReturnStmtClass => return transReturnStmt(c, scope, @as(*const clang.ReturnStmt, @ptrCast(stmt))), 1156 .StringLiteralClass => return transStringLiteral(c, scope, @as(*const clang.StringLiteral, @ptrCast(stmt)), result_used), 1157 .ParenExprClass => { 1158 const expr = try transExpr(c, scope, @as(*const clang.ParenExpr, @ptrCast(stmt)).getSubExpr(), .used); 1159 return maybeSuppressResult(c, result_used, expr); 1160 }, 1161 .InitListExprClass => return transInitListExpr(c, scope, @as(*const clang.InitListExpr, @ptrCast(stmt)), result_used), 1162 .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @as(*const clang.Expr, @ptrCast(stmt))), 1163 .IfStmtClass => return transIfStmt(c, scope, @as(*const clang.IfStmt, @ptrCast(stmt))), 1164 .WhileStmtClass => return transWhileLoop(c, scope, @as(*const clang.WhileStmt, @ptrCast(stmt))), 1165 .DoStmtClass => return transDoWhileLoop(c, scope, @as(*const clang.DoStmt, @ptrCast(stmt))), 1166 .NullStmtClass => { 1167 return Tag.empty_block.init(); 1168 }, 1169 .ContinueStmtClass => return Tag.@"continue".init(), 1170 .BreakStmtClass => return Tag.@"break".init(), 1171 .ForStmtClass => return transForLoop(c, scope, @as(*const clang.ForStmt, @ptrCast(stmt))), 1172 .FloatingLiteralClass => return transFloatingLiteral(c, @as(*const clang.FloatingLiteral, @ptrCast(stmt)), result_used), 1173 .ConditionalOperatorClass => { 1174 return transConditionalOperator(c, scope, @as(*const clang.ConditionalOperator, @ptrCast(stmt)), result_used); 1175 }, 1176 .BinaryConditionalOperatorClass => { 1177 return transBinaryConditionalOperator(c, scope, @as(*const clang.BinaryConditionalOperator, @ptrCast(stmt)), result_used); 1178 }, 1179 .SwitchStmtClass => return transSwitch(c, scope, @as(*const clang.SwitchStmt, @ptrCast(stmt))), 1180 .CaseStmtClass, .DefaultStmtClass => { 1181 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{}); 1182 }, 1183 .ConstantExprClass => return transConstantExpr(c, scope, @as(*const clang.Expr, @ptrCast(stmt)), result_used), 1184 .PredefinedExprClass => return transPredefinedExpr(c, scope, @as(*const clang.PredefinedExpr, @ptrCast(stmt)), result_used), 1185 .CharacterLiteralClass => return transCharLiteral(c, scope, @as(*const clang.CharacterLiteral, @ptrCast(stmt)), result_used, .with_as), 1186 .StmtExprClass => return transStmtExpr(c, scope, @as(*const clang.StmtExpr, @ptrCast(stmt)), result_used), 1187 .MemberExprClass => return transMemberExpr(c, scope, @as(*const clang.MemberExpr, @ptrCast(stmt)), result_used), 1188 .ArraySubscriptExprClass => return transArrayAccess(c, scope, @as(*const clang.ArraySubscriptExpr, @ptrCast(stmt)), result_used), 1189 .CallExprClass => return transCallExpr(c, scope, @as(*const clang.CallExpr, @ptrCast(stmt)), result_used), 1190 .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @as(*const clang.UnaryExprOrTypeTraitExpr, @ptrCast(stmt)), result_used), 1191 .UnaryOperatorClass => return transUnaryOperator(c, scope, @as(*const clang.UnaryOperator, @ptrCast(stmt)), result_used), 1192 .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @as(*const clang.CompoundAssignOperator, @ptrCast(stmt)), result_used), 1193 .OpaqueValueExprClass => { 1194 const source_expr = @as(*const clang.OpaqueValueExpr, @ptrCast(stmt)).getSourceExpr().?; 1195 const expr = try transExpr(c, scope, source_expr, .used); 1196 return maybeSuppressResult(c, result_used, expr); 1197 }, 1198 .OffsetOfExprClass => return transOffsetOfExpr(c, @as(*const clang.OffsetOfExpr, @ptrCast(stmt)), result_used), 1199 .CompoundLiteralExprClass => { 1200 const compound_literal = @as(*const clang.CompoundLiteralExpr, @ptrCast(stmt)); 1201 return transExpr(c, scope, compound_literal.getInitializer(), result_used); 1202 }, 1203 .GenericSelectionExprClass => { 1204 const gen_sel = @as(*const clang.GenericSelectionExpr, @ptrCast(stmt)); 1205 return transExpr(c, scope, gen_sel.getResultExpr(), result_used); 1206 }, 1207 .ConvertVectorExprClass => { 1208 const conv_vec = @as(*const clang.ConvertVectorExpr, @ptrCast(stmt)); 1209 const conv_vec_node = try transConvertVectorExpr(c, scope, conv_vec); 1210 return maybeSuppressResult(c, result_used, conv_vec_node); 1211 }, 1212 .ShuffleVectorExprClass => { 1213 const shuffle_vec_expr = @as(*const clang.ShuffleVectorExpr, @ptrCast(stmt)); 1214 const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr); 1215 return maybeSuppressResult(c, result_used, shuffle_vec_node); 1216 }, 1217 .ChooseExprClass => { 1218 const choose_expr = @as(*const clang.ChooseExpr, @ptrCast(stmt)); 1219 return transExpr(c, scope, choose_expr.getChosenSubExpr(), result_used); 1220 }, 1221 // When adding new cases here, see comment for maybeBlockify() 1222 .GCCAsmStmtClass, 1223 .GotoStmtClass, 1224 .IndirectGotoStmtClass, 1225 .AttributedStmtClass, 1226 .AddrLabelExprClass, 1227 .AtomicExprClass, 1228 .BlockExprClass, 1229 .UserDefinedLiteralClass, 1230 .BuiltinBitCastExprClass, 1231 .DesignatedInitExprClass, 1232 .LabelStmtClass, 1233 => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}), 1234 else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported stmt class {s}", .{@tagName(sc)}), 1235 } 1236 } 1237 1238 /// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-convertvector 1239 fn transConvertVectorExpr( 1240 c: *Context, 1241 scope: *Scope, 1242 expr: *const clang.ConvertVectorExpr, 1243 ) TransError!Node { 1244 const base_stmt = @as(*const clang.Stmt, @ptrCast(expr)); 1245 1246 var block_scope = try Scope.Block.init(c, scope, true); 1247 defer block_scope.deinit(); 1248 1249 const src_expr = expr.getSrcExpr(); 1250 const src_type = qualTypeCanon(src_expr.getType()); 1251 const src_vector_ty = @as(*const clang.VectorType, @ptrCast(src_type)); 1252 const src_element_qt = src_vector_ty.getElementType(); 1253 1254 const src_expr_node = try transExpr(c, &block_scope.base, src_expr, .used); 1255 1256 const dst_qt = expr.getTypeSourceInfo_getType(); 1257 const dst_type_node = try transQualType(c, &block_scope.base, dst_qt, base_stmt.getBeginLoc()); 1258 const dst_vector_ty = @as(*const clang.VectorType, @ptrCast(qualTypeCanon(dst_qt))); 1259 const num_elements = dst_vector_ty.getNumElements(); 1260 const dst_element_qt = dst_vector_ty.getElementType(); 1261 1262 // workaround for https://github.com/ziglang/zig/issues/8322 1263 // we store the casted results into temp variables and use those 1264 // to initialize the vector. Eventually we can just directly 1265 // construct the init_list from casted source members 1266 var i: usize = 0; 1267 while (i < num_elements) : (i += 1) { 1268 const mangled_name = try block_scope.makeMangledName(c, "tmp"); 1269 const value = try Tag.array_access.create(c.arena, .{ 1270 .lhs = src_expr_node, 1271 .rhs = try transCreateNodeNumber(c, i, .int), 1272 }); 1273 const tmp_decl_node = try Tag.var_simple.create(c.arena, .{ 1274 .name = mangled_name, 1275 .init = try transCCast(c, &block_scope.base, base_stmt.getBeginLoc(), dst_element_qt, src_element_qt, value), 1276 }); 1277 try block_scope.statements.append(tmp_decl_node); 1278 } 1279 1280 const init_list = try c.arena.alloc(Node, num_elements); 1281 for (init_list, 0..) |*init, init_index| { 1282 const tmp_decl = block_scope.statements.items[init_index]; 1283 const name = tmp_decl.castTag(.var_simple).?.data.name; 1284 init.* = try Tag.identifier.create(c.arena, name); 1285 } 1286 1287 const vec_init = try Tag.array_init.create(c.arena, .{ 1288 .cond = dst_type_node, 1289 .cases = init_list, 1290 }); 1291 1292 const break_node = try Tag.break_val.create(c.arena, .{ 1293 .label = block_scope.label, 1294 .val = vec_init, 1295 }); 1296 try block_scope.statements.append(break_node); 1297 return block_scope.complete(c); 1298 } 1299 1300 fn makeShuffleMask(c: *Context, scope: *Scope, expr: *const clang.ShuffleVectorExpr, vector_len: Node) TransError!Node { 1301 const num_subexprs = expr.getNumSubExprs(); 1302 assert(num_subexprs >= 3); // two source vectors + at least 1 index expression 1303 const mask_len = num_subexprs - 2; 1304 1305 const mask_type = try Tag.vector.create(c.arena, .{ 1306 .lhs = try transCreateNodeNumber(c, mask_len, .int), 1307 .rhs = try Tag.type.create(c.arena, "i32"), 1308 }); 1309 1310 const init_list = try c.arena.alloc(Node, mask_len); 1311 1312 for (init_list, 0..) |*init, i| { 1313 const index_expr = try transExprCoercing(c, scope, expr.getExpr(@as(c_uint, @intCast(i + 2))), .used); 1314 const converted_index = try Tag.helpers_shuffle_vector_index.create(c.arena, .{ .lhs = index_expr, .rhs = vector_len }); 1315 init.* = converted_index; 1316 } 1317 1318 return Tag.array_init.create(c.arena, .{ 1319 .cond = mask_type, 1320 .cases = init_list, 1321 }); 1322 } 1323 1324 /// @typeInfo(@TypeOf(vec_node)).Vector.<field> 1325 fn vectorTypeInfo(arena: mem.Allocator, vec_node: Node, field: []const u8) TransError!Node { 1326 const typeof_call = try Tag.typeof.create(arena, vec_node); 1327 const typeinfo_call = try Tag.typeinfo.create(arena, typeof_call); 1328 const vector_type_info = try Tag.field_access.create(arena, .{ .lhs = typeinfo_call, .field_name = "Vector" }); 1329 return Tag.field_access.create(arena, .{ .lhs = vector_type_info, .field_name = field }); 1330 } 1331 1332 fn transShuffleVectorExpr( 1333 c: *Context, 1334 scope: *Scope, 1335 expr: *const clang.ShuffleVectorExpr, 1336 ) TransError!Node { 1337 const base_expr = @as(*const clang.Expr, @ptrCast(expr)); 1338 const num_subexprs = expr.getNumSubExprs(); 1339 if (num_subexprs < 3) return fail(c, error.UnsupportedTranslation, base_expr.getBeginLoc(), "ShuffleVector needs at least 1 index", .{}); 1340 1341 const a = try transExpr(c, scope, expr.getExpr(0), .used); 1342 const b = try transExpr(c, scope, expr.getExpr(1), .used); 1343 1344 // clang requires first two arguments to __builtin_shufflevector to be same type 1345 const vector_child_type = try vectorTypeInfo(c.arena, a, "child"); 1346 const vector_len = try vectorTypeInfo(c.arena, a, "len"); 1347 const shuffle_mask = try makeShuffleMask(c, scope, expr, vector_len); 1348 1349 return Tag.shuffle.create(c.arena, .{ 1350 .element_type = vector_child_type, 1351 .a = a, 1352 .b = b, 1353 .mask_vector = shuffle_mask, 1354 }); 1355 } 1356 1357 /// Translate a "simple" offsetof expression containing exactly one component, 1358 /// when that component is of kind .Field - e.g. offsetof(mytype, myfield) 1359 fn transSimpleOffsetOfExpr(c: *Context, expr: *const clang.OffsetOfExpr) TransError!Node { 1360 assert(expr.getNumComponents() == 1); 1361 const component = expr.getComponent(0); 1362 if (component.getKind() == .Field) { 1363 const field_decl = component.getField(); 1364 if (field_decl.getParent()) |record_decl| { 1365 if (c.decl_table.get(@intFromPtr(record_decl.getCanonicalDecl()))) |type_name| { 1366 const type_node = try Tag.type.create(c.arena, type_name); 1367 1368 const raw_field_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin()); 1369 const quoted_field_name = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{raw_field_name}); 1370 const field_name_node = try Tag.string_literal.create(c.arena, quoted_field_name); 1371 1372 return Tag.offset_of.create(c.arena, .{ 1373 .lhs = type_node, 1374 .rhs = field_name_node, 1375 }); 1376 } 1377 } 1378 } 1379 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "failed to translate simple OffsetOfExpr", .{}); 1380 } 1381 1382 fn transOffsetOfExpr( 1383 c: *Context, 1384 expr: *const clang.OffsetOfExpr, 1385 result_used: ResultUsed, 1386 ) TransError!Node { 1387 if (expr.getNumComponents() == 1) { 1388 const offsetof_expr = try transSimpleOffsetOfExpr(c, expr); 1389 return maybeSuppressResult(c, result_used, offsetof_expr); 1390 } 1391 1392 // TODO implement OffsetOfExpr with more than 1 component 1393 // OffsetOfExpr API: 1394 // call expr.getComponent(idx) while idx < expr.getNumComponents() 1395 // component.getKind() will be either .Array or .Field (other kinds are C++-only) 1396 // if .Field, use component.getField() to retrieve *clang.FieldDecl 1397 // if .Array, use component.getArrayExprIndex() to get a c_uint which 1398 // can be passed to expr.getIndexExpr(expr_index) to get the *clang.Expr for the array index 1399 1400 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{}); 1401 } 1402 1403 /// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers 1404 /// will become very large positive numbers but that is ok since we only use this in 1405 /// pointer arithmetic expressions, where wraparound will ensure we get the correct value. 1406 /// node -> @bitCast(usize, @intCast(isize, node)) 1407 fn usizeCastForWrappingPtrArithmetic(gpa: mem.Allocator, node: Node) TransError!Node { 1408 const intcast_node = try Tag.as.create(gpa, .{ 1409 .lhs = try Tag.type.create(gpa, "isize"), 1410 .rhs = try Tag.int_cast.create(gpa, node), 1411 }); 1412 1413 return Tag.as.create(gpa, .{ 1414 .lhs = try Tag.type.create(gpa, "usize"), 1415 .rhs = try Tag.bit_cast.create(gpa, intcast_node), 1416 }); 1417 } 1418 1419 /// Translate an arithmetic expression with a pointer operand and a signed-integer operand. 1420 /// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then 1421 /// bitcast to usize; pointer wraparound make the math work. 1422 /// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left. 1423 /// The + operator in C is not a sequence point so it should be safe to switch the order if necessary. 1424 fn transCreatePointerArithmeticSignedOp( 1425 c: *Context, 1426 scope: *Scope, 1427 stmt: *const clang.BinaryOperator, 1428 result_used: ResultUsed, 1429 ) TransError!Node { 1430 const is_add = stmt.getOpcode() == .Add; 1431 const lhs = stmt.getLHS(); 1432 const rhs = stmt.getRHS(); 1433 const swap_operands = is_add and cIsSignedInteger(getExprQualType(c, lhs)); 1434 1435 const swizzled_lhs = if (swap_operands) rhs else lhs; 1436 const swizzled_rhs = if (swap_operands) lhs else rhs; 1437 1438 const lhs_node = try transExpr(c, scope, swizzled_lhs, .used); 1439 const rhs_node = try transExpr(c, scope, swizzled_rhs, .used); 1440 1441 const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); 1442 1443 return transCreateNodeInfixOp( 1444 c, 1445 if (is_add) .add else .sub, 1446 lhs_node, 1447 bitcast_node, 1448 result_used, 1449 ); 1450 } 1451 1452 fn transBinaryOperator( 1453 c: *Context, 1454 scope: *Scope, 1455 stmt: *const clang.BinaryOperator, 1456 result_used: ResultUsed, 1457 ) TransError!Node { 1458 const op = stmt.getOpcode(); 1459 const qt = stmt.getType(); 1460 const isPointerDiffExpr = cIsPointerDiffExpr(stmt); 1461 switch (op) { 1462 .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), 1463 .Comma => { 1464 var block_scope = try Scope.Block.init(c, scope, true); 1465 defer block_scope.deinit(); 1466 1467 const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused); 1468 try block_scope.statements.append(lhs); 1469 1470 const rhs = try transExpr(c, &block_scope.base, stmt.getRHS(), .used); 1471 const break_node = try Tag.break_val.create(c.arena, .{ 1472 .label = block_scope.label, 1473 .val = rhs, 1474 }); 1475 try block_scope.statements.append(break_node); 1476 const block_node = try block_scope.complete(c); 1477 return maybeSuppressResult(c, result_used, block_node); 1478 }, 1479 .Div => { 1480 if (cIsSignedInteger(qt)) { 1481 // signed integer division uses @divTrunc 1482 const lhs = try transExpr(c, scope, stmt.getLHS(), .used); 1483 const rhs = try transExpr(c, scope, stmt.getRHS(), .used); 1484 const div_trunc = try Tag.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 1485 return maybeSuppressResult(c, result_used, div_trunc); 1486 } 1487 }, 1488 .Rem => { 1489 if (cIsSignedInteger(qt)) { 1490 // signed integer remainder uses std.zig.c_translation.signedRemainder 1491 const lhs = try transExpr(c, scope, stmt.getLHS(), .used); 1492 const rhs = try transExpr(c, scope, stmt.getRHS(), .used); 1493 const rem = try Tag.signed_remainder.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 1494 return maybeSuppressResult(c, result_used, rem); 1495 } 1496 }, 1497 .Shl => { 1498 return transCreateNodeShiftOp(c, scope, stmt, .shl, result_used); 1499 }, 1500 .Shr => { 1501 return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used); 1502 }, 1503 .LAnd => { 1504 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"and", result_used); 1505 }, 1506 .LOr => { 1507 return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used); 1508 }, 1509 .Add, .Sub => { 1510 // `ptr + idx` and `idx + ptr` -> ptr + @bitCast(usize, @intCast(isize, idx)) 1511 // `ptr - idx` -> ptr - @bitCast(usize, @intCast(isize, idx)) 1512 if (qualTypeIsPtr(qt) and (cIsSignedInteger(getExprQualType(c, stmt.getLHS())) or 1513 cIsSignedInteger(getExprQualType(c, stmt.getRHS())))) return transCreatePointerArithmeticSignedOp(c, scope, stmt, result_used); 1514 }, 1515 else => {}, 1516 } 1517 var op_id: Tag = undefined; 1518 switch (op) { 1519 .Add => { 1520 if (cIsUnsignedInteger(qt)) { 1521 op_id = .add_wrap; 1522 } else { 1523 op_id = .add; 1524 } 1525 }, 1526 .Sub => { 1527 if (cIsUnsignedInteger(qt) or isPointerDiffExpr) { 1528 op_id = .sub_wrap; 1529 } else { 1530 op_id = .sub; 1531 } 1532 }, 1533 .Mul => { 1534 if (cIsUnsignedInteger(qt)) { 1535 op_id = .mul_wrap; 1536 } else { 1537 op_id = .mul; 1538 } 1539 }, 1540 .Div => { 1541 // unsigned/float division uses the operator 1542 op_id = .div; 1543 }, 1544 .Rem => { 1545 // unsigned/float division uses the operator 1546 op_id = .mod; 1547 }, 1548 .LT => { 1549 op_id = .less_than; 1550 }, 1551 .GT => { 1552 op_id = .greater_than; 1553 }, 1554 .LE => { 1555 op_id = .less_than_equal; 1556 }, 1557 .GE => { 1558 op_id = .greater_than_equal; 1559 }, 1560 .EQ => { 1561 op_id = .equal; 1562 }, 1563 .NE => { 1564 op_id = .not_equal; 1565 }, 1566 .And => { 1567 op_id = .bit_and; 1568 }, 1569 .Xor => { 1570 op_id = .bit_xor; 1571 }, 1572 .Or => { 1573 op_id = .bit_or; 1574 }, 1575 else => unreachable, 1576 } 1577 1578 const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used); 1579 const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used); 1580 1581 const lhs = if (isBoolRes(lhs_uncasted)) 1582 try Tag.int_from_bool.create(c.arena, lhs_uncasted) 1583 else if (isPointerDiffExpr) 1584 try Tag.int_from_ptr.create(c.arena, lhs_uncasted) 1585 else 1586 lhs_uncasted; 1587 1588 const rhs = if (isBoolRes(rhs_uncasted)) 1589 try Tag.int_from_bool.create(c.arena, rhs_uncasted) 1590 else if (isPointerDiffExpr) 1591 try Tag.int_from_ptr.create(c.arena, rhs_uncasted) 1592 else 1593 rhs_uncasted; 1594 1595 const infixOpNode = try transCreateNodeInfixOp(c, op_id, lhs, rhs, result_used); 1596 if (isPointerDiffExpr) { 1597 // @divExact(@bitCast(<platform-ptrdiff_t>, @intFromPtr(lhs) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>)) 1598 const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true); 1599 1600 // C standard requires that pointer subtraction operands are of the same type, 1601 // otherwise it is undefined behavior. So we can assume the left and right 1602 // sides are the same QualType and arbitrarily choose left. 1603 const lhs_expr = stmt.getLHS(); 1604 const lhs_qt = getExprQualType(c, lhs_expr); 1605 const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc()); 1606 const c_pointer = getContainer(c, lhs_qt_translated).?; 1607 const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type; 1608 const sizeof = try Tag.sizeof.create(c.arena, elem_type); 1609 1610 const bitcast = try Tag.as.create(c.arena, .{ 1611 .lhs = ptrdiff_type, 1612 .rhs = try Tag.bit_cast.create(c.arena, infixOpNode), 1613 }); 1614 1615 return Tag.div_exact.create(c.arena, .{ 1616 .lhs = bitcast, 1617 .rhs = sizeof, 1618 }); 1619 } 1620 return infixOpNode; 1621 } 1622 1623 fn transCompoundStmtInline( 1624 c: *Context, 1625 stmt: *const clang.CompoundStmt, 1626 block: *Scope.Block, 1627 ) TransError!void { 1628 var it = stmt.body_begin(); 1629 const end_it = stmt.body_end(); 1630 while (it != end_it) : (it += 1) { 1631 const result = try transStmt(c, &block.base, it[0], .unused); 1632 switch (result.tag()) { 1633 .declaration, .empty_block => {}, 1634 else => try block.statements.append(result), 1635 } 1636 } 1637 } 1638 1639 fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { 1640 var block_scope = try Scope.Block.init(c, scope, false); 1641 defer block_scope.deinit(); 1642 try transCompoundStmtInline(c, stmt, &block_scope); 1643 return try block_scope.complete(c); 1644 } 1645 1646 fn transCStyleCastExprClass( 1647 c: *Context, 1648 scope: *Scope, 1649 stmt: *const clang.CStyleCastExpr, 1650 result_used: ResultUsed, 1651 ) TransError!Node { 1652 const cast_expr = @as(*const clang.CastExpr, @ptrCast(stmt)); 1653 const sub_expr = stmt.getSubExpr(); 1654 const dst_type = stmt.getType(); 1655 const src_type = sub_expr.getType(); 1656 const sub_expr_node = try transExpr(c, scope, sub_expr, .used); 1657 const loc = stmt.getBeginLoc(); 1658 1659 const cast_node = if (cast_expr.getCastKind() == .ToUnion) blk: { 1660 const field_decl = cast_expr.getTargetFieldForToUnionCast(dst_type, src_type).?; // C syntax error if target field is null 1661 const field_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin()); 1662 1663 const union_ty = try transQualType(c, scope, dst_type, loc); 1664 1665 const inits = [1]ast.Payload.ContainerInit.Initializer{.{ .name = field_name, .value = sub_expr_node }}; 1666 break :blk try Tag.container_init.create(c.arena, .{ 1667 .lhs = union_ty, 1668 .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, &inits), 1669 }); 1670 } else (try transCCast( 1671 c, 1672 scope, 1673 loc, 1674 dst_type, 1675 src_type, 1676 sub_expr_node, 1677 )); 1678 return maybeSuppressResult(c, result_used, cast_node); 1679 } 1680 1681 /// The alignment of a variable or field 1682 const ClangAlignment = struct { 1683 /// Clang reports the alignment in bits, we use bytes 1684 /// Clang uses 0 for "no alignment specified", we use null 1685 bit_alignment: c_uint, 1686 /// If the field or variable is marked as 'packed' 1687 /// 1688 /// According to the GCC variable attribute docs, this impacts alignment 1689 /// https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html 1690 /// 1691 /// > The packed attribute specifies that a structure member 1692 /// > should have the smallest possible alignment 1693 /// 1694 /// Note also that specifying the 'packed' attribute on a structure 1695 /// implicitly packs all its fields (making their alignment 1). 1696 /// 1697 /// This will be null if the AST node doesn't support packing (functions) 1698 is_packed: ?bool, 1699 1700 /// Get the alignment for a field, optionally taking into account the parent record 1701 pub fn forField(c: *const Context, field: *const clang.FieldDecl, parent: ?*const clang.RecordDecl) ClangAlignment { 1702 const parent_packed = if (parent) |record| record.getPackedAttribute() else false; 1703 // NOTE: According to GCC docs, parent attribute packed implies child attribute packed 1704 return ClangAlignment{ 1705 .bit_alignment = field.getAlignedAttribute(c.clang_context), 1706 .is_packed = field.getPackedAttribute() or parent_packed, 1707 }; 1708 } 1709 1710 pub fn forVar(c: *const Context, var_decl: *const clang.VarDecl) ClangAlignment { 1711 return ClangAlignment{ 1712 .bit_alignment = var_decl.getAlignedAttribute(c.clang_context), 1713 .is_packed = var_decl.getPackedAttribute(), 1714 }; 1715 } 1716 1717 pub fn forFunc(c: *const Context, fun: *const clang.FunctionDecl) ClangAlignment { 1718 return ClangAlignment{ 1719 .bit_alignment = fun.getAlignedAttribute(c.clang_context), 1720 .is_packed = null, // not supported by GCC/clang (or meaningful), 1721 }; 1722 } 1723 1724 /// Translate the clang alignment info into a zig alignment 1725 /// 1726 /// Returns null if there is no special alignment info 1727 pub fn zigAlignment(self: ClangAlignment) ?c_uint { 1728 if (self.bit_alignment != 0) { 1729 return self.bit_alignment / 8; 1730 } else if (self.is_packed orelse false) { 1731 return 1; 1732 } else { 1733 return null; 1734 } 1735 } 1736 }; 1737 1738 fn transDeclStmtOne( 1739 c: *Context, 1740 scope: *Scope, 1741 decl: *const clang.Decl, 1742 block_scope: *Scope.Block, 1743 ) TransError!void { 1744 switch (decl.getKind()) { 1745 .Var => { 1746 const var_decl = @as(*const clang.VarDecl, @ptrCast(decl)); 1747 const decl_init = var_decl.getInit(); 1748 const loc = decl.getLocation(); 1749 1750 const qual_type = var_decl.getTypeSourceInfo_getType(); 1751 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin()); 1752 const mangled_name = try block_scope.makeMangledName(c, name); 1753 1754 if (var_decl.getStorageClass() == .Extern) { 1755 // This is actually a global variable, put it in the global scope and reference it. 1756 // `_ = mangled_name;` 1757 return visitVarDecl(c, var_decl, mangled_name); 1758 } else if (qualTypeWasDemotedToOpaque(c, qual_type)) { 1759 return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{}); 1760 } 1761 1762 const is_static_local = var_decl.isStaticLocal(); 1763 const is_const = qual_type.isConstQualified(); 1764 const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc); 1765 1766 var init_node = if (decl_init) |expr| 1767 if (expr.getStmtClass() == .StringLiteralClass) 1768 try transStringLiteralInitializer(c, @as(*const clang.StringLiteral, @ptrCast(expr)), type_node) 1769 else 1770 try transExprCoercing(c, scope, expr, .used) 1771 else if (is_static_local) 1772 try Tag.std_mem_zeroes.create(c.arena, type_node) 1773 else 1774 Tag.undefined_literal.init(); 1775 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { 1776 init_node = try Tag.int_from_bool.create(c.arena, init_node); 1777 } else if (init_node.tag() == .string_literal and qualTypeIsCharStar(qual_type)) { 1778 const dst_type_node = try transQualType(c, scope, qual_type, loc); 1779 init_node = try removeCVQualifiers(c, dst_type_node, init_node); 1780 } 1781 1782 const var_name: []const u8 = if (is_static_local) Scope.Block.static_inner_name else mangled_name; 1783 var node = try Tag.var_decl.create(c.arena, .{ 1784 .is_pub = false, 1785 .is_const = is_const, 1786 .is_extern = false, 1787 .is_export = false, 1788 .is_threadlocal = var_decl.getTLSKind() != .None, 1789 .linksection_string = null, 1790 .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(), 1791 .name = var_name, 1792 .type = type_node, 1793 .init = init_node, 1794 }); 1795 if (is_static_local) { 1796 node = try Tag.static_local_var.create(c.arena, .{ .name = mangled_name, .init = node }); 1797 } 1798 try block_scope.statements.append(node); 1799 try block_scope.discardVariable(c, mangled_name); 1800 1801 const cleanup_attr = var_decl.getCleanupAttribute(); 1802 if (cleanup_attr) |fn_decl| { 1803 const cleanup_fn_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(fn_decl)).getName_bytes_begin()); 1804 const fn_id = try Tag.identifier.create(c.arena, cleanup_fn_name); 1805 1806 const varname = try Tag.identifier.create(c.arena, mangled_name); 1807 const args = try c.arena.alloc(Node, 1); 1808 args[0] = try Tag.address_of.create(c.arena, varname); 1809 1810 const cleanup_call = try Tag.call.create(c.arena, .{ .lhs = fn_id, .args = args }); 1811 const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = cleanup_call }); 1812 const deferred_cleanup = try Tag.@"defer".create(c.arena, discard); 1813 1814 try block_scope.statements.append(deferred_cleanup); 1815 } 1816 }, 1817 .Typedef => { 1818 try transTypeDef(c, scope, @as(*const clang.TypedefNameDecl, @ptrCast(decl))); 1819 }, 1820 .Record => { 1821 try transRecordDecl(c, scope, @as(*const clang.RecordDecl, @ptrCast(decl))); 1822 }, 1823 .Enum => { 1824 try transEnumDecl(c, scope, @as(*const clang.EnumDecl, @ptrCast(decl))); 1825 }, 1826 .Function => { 1827 try visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl))); 1828 }, 1829 else => { 1830 const decl_name = try c.str(decl.getDeclKindName()); 1831 try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); 1832 }, 1833 } 1834 } 1835 1836 fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node { 1837 const block_scope = try scope.findBlockScope(c); 1838 1839 var it = stmt.decl_begin(); 1840 const end_it = stmt.decl_end(); 1841 while (it != end_it) : (it += 1) { 1842 try transDeclStmtOne(c, scope, it[0], block_scope); 1843 } 1844 return Tag.declaration.init(); 1845 } 1846 1847 fn transDeclRefExpr( 1848 c: *Context, 1849 scope: *Scope, 1850 expr: *const clang.DeclRefExpr, 1851 ) TransError!Node { 1852 const value_decl = expr.getDecl(); 1853 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin()); 1854 const mangled_name = scope.getAlias(name); 1855 var ref_expr = if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr)))) 1856 try Tag.fn_identifier.create(c.arena, mangled_name) 1857 else 1858 try Tag.identifier.create(c.arena, mangled_name); 1859 1860 if (@as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var) { 1861 const var_decl = @as(*const clang.VarDecl, @ptrCast(value_decl)); 1862 if (var_decl.isStaticLocal()) { 1863 ref_expr = try Tag.field_access.create(c.arena, .{ 1864 .lhs = ref_expr, 1865 .field_name = Scope.Block.static_inner_name, 1866 }); 1867 } 1868 } 1869 scope.skipVariableDiscard(mangled_name); 1870 return ref_expr; 1871 } 1872 1873 fn transImplicitCastExpr( 1874 c: *Context, 1875 scope: *Scope, 1876 expr: *const clang.ImplicitCastExpr, 1877 result_used: ResultUsed, 1878 ) TransError!Node { 1879 const sub_expr = expr.getSubExpr(); 1880 const dest_type = getExprQualType(c, @as(*const clang.Expr, @ptrCast(expr))); 1881 const src_type = getExprQualType(c, sub_expr); 1882 switch (expr.getCastKind()) { 1883 .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => { 1884 const sub_expr_node = try transExpr(c, scope, sub_expr, .used); 1885 const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node); 1886 return maybeSuppressResult(c, result_used, casted); 1887 }, 1888 .LValueToRValue, .NoOp, .FunctionToPointerDecay => { 1889 const sub_expr_node = try transExpr(c, scope, sub_expr, .used); 1890 return maybeSuppressResult(c, result_used, sub_expr_node); 1891 }, 1892 .ArrayToPointerDecay => { 1893 const sub_expr_node = try transExpr(c, scope, sub_expr, .used); 1894 if (exprIsNarrowStringLiteral(sub_expr) or exprIsFlexibleArrayRef(c, sub_expr)) { 1895 return maybeSuppressResult(c, result_used, sub_expr_node); 1896 } 1897 1898 const addr = try Tag.address_of.create(c.arena, sub_expr_node); 1899 const casted = try transCPtrCast(c, scope, expr.getBeginLoc(), dest_type, src_type, addr); 1900 return maybeSuppressResult(c, result_used, casted); 1901 }, 1902 .NullToPointer => { 1903 return Tag.null_literal.init(); 1904 }, 1905 .PointerToBoolean => { 1906 // @intFromPtr(val) != 0 1907 const ptr_node = try transExpr(c, scope, sub_expr, .used); 1908 const int_from_ptr = try Tag.int_from_ptr.create(c.arena, ptr_node); 1909 1910 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() }); 1911 return maybeSuppressResult(c, result_used, ne); 1912 }, 1913 .IntegralToBoolean, .FloatingToBoolean => { 1914 const sub_expr_node = try transExpr(c, scope, sub_expr, .used); 1915 1916 // The expression is already a boolean one, return it as-is 1917 if (isBoolRes(sub_expr_node)) 1918 return maybeSuppressResult(c, result_used, sub_expr_node); 1919 1920 // val != 0 1921 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Tag.zero_literal.init() }); 1922 return maybeSuppressResult(c, result_used, ne); 1923 }, 1924 .BuiltinFnToFnPtr => { 1925 return transBuiltinFnExpr(c, scope, sub_expr, result_used); 1926 }, 1927 .ToVoid => { 1928 // Should only appear in the rhs and lhs of a ConditionalOperator 1929 return transExpr(c, scope, sub_expr, .unused); 1930 }, 1931 else => |kind| return fail( 1932 c, 1933 error.UnsupportedTranslation, 1934 @as(*const clang.Stmt, @ptrCast(expr)).getBeginLoc(), 1935 "unsupported CastKind {s}", 1936 .{@tagName(kind)}, 1937 ), 1938 } 1939 } 1940 1941 fn isBuiltinDefined(name: []const u8) bool { 1942 inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| { 1943 if (std.mem.eql(u8, name, decl.name)) return true; 1944 } 1945 return false; 1946 } 1947 1948 fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { 1949 const node = try transExpr(c, scope, expr, used); 1950 if (node.castTag(.fn_identifier)) |ident| { 1951 const name = ident.data; 1952 if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.zig.c_builtins", .{name}); 1953 } 1954 return node; 1955 } 1956 1957 fn transBoolExpr( 1958 c: *Context, 1959 scope: *Scope, 1960 expr: *const clang.Expr, 1961 used: ResultUsed, 1962 ) TransError!Node { 1963 if (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass() == .IntegerLiteralClass) { 1964 var signum: c_int = undefined; 1965 if (!(@as(*const clang.IntegerLiteral, @ptrCast(expr)).getSignum(&signum, c.clang_context))) { 1966 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); 1967 } 1968 const is_zero = signum == 0; 1969 return Node{ .tag_if_small_enough = @intFromEnum(([2]Tag{ .true_literal, .false_literal })[@intFromBool(is_zero)]) }; 1970 } 1971 1972 const res = try transExpr(c, scope, expr, used); 1973 if (isBoolRes(res)) { 1974 return maybeSuppressResult(c, used, res); 1975 } 1976 1977 const ty = getExprQualType(c, expr).getTypePtr(); 1978 const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used); 1979 1980 return maybeSuppressResult(c, used, node); 1981 } 1982 1983 fn exprIsBooleanType(expr: *const clang.Expr) bool { 1984 return qualTypeIsBoolean(expr.getType()); 1985 } 1986 1987 fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { 1988 switch (expr.getStmtClass()) { 1989 .StringLiteralClass => { 1990 const string_lit = @as(*const clang.StringLiteral, @ptrCast(expr)); 1991 return string_lit.getCharByteWidth() == 1; 1992 }, 1993 .PredefinedExprClass => return true, 1994 .UnaryOperatorClass => { 1995 const op_expr = @as(*const clang.UnaryOperator, @ptrCast(expr)).getSubExpr(); 1996 return exprIsNarrowStringLiteral(op_expr); 1997 }, 1998 .ParenExprClass => { 1999 const op_expr = @as(*const clang.ParenExpr, @ptrCast(expr)).getSubExpr(); 2000 return exprIsNarrowStringLiteral(op_expr); 2001 }, 2002 .GenericSelectionExprClass => { 2003 const gen_sel = @as(*const clang.GenericSelectionExpr, @ptrCast(expr)); 2004 return exprIsNarrowStringLiteral(gen_sel.getResultExpr()); 2005 }, 2006 else => return false, 2007 } 2008 } 2009 2010 fn exprIsFlexibleArrayRef(c: *Context, expr: *const clang.Expr) bool { 2011 if (expr.getStmtClass() == .MemberExprClass) { 2012 const member_expr = @as(*const clang.MemberExpr, @ptrCast(expr)); 2013 const member_decl = member_expr.getMemberDecl(); 2014 const decl_kind = @as(*const clang.Decl, @ptrCast(member_decl)).getKind(); 2015 if (decl_kind == .Field) { 2016 const field_decl = @as(*const clang.FieldDecl, @ptrCast(member_decl)); 2017 return isFlexibleArrayFieldDecl(c, field_decl); 2018 } 2019 } 2020 return false; 2021 } 2022 2023 fn isBoolRes(res: Node) bool { 2024 switch (res.tag()) { 2025 .@"or", 2026 .@"and", 2027 .equal, 2028 .not_equal, 2029 .less_than, 2030 .less_than_equal, 2031 .greater_than, 2032 .greater_than_equal, 2033 .not, 2034 .false_literal, 2035 .true_literal, 2036 => return true, 2037 else => return false, 2038 } 2039 } 2040 2041 fn finishBoolExpr( 2042 c: *Context, 2043 scope: *Scope, 2044 loc: clang.SourceLocation, 2045 ty: *const clang.Type, 2046 node: Node, 2047 used: ResultUsed, 2048 ) TransError!Node { 2049 switch (ty.getTypeClass()) { 2050 .Builtin => { 2051 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 2052 2053 switch (builtin_ty.getKind()) { 2054 .Bool => return node, 2055 .Char_U, 2056 .UChar, 2057 .Char_S, 2058 .SChar, 2059 .UShort, 2060 .UInt, 2061 .ULong, 2062 .ULongLong, 2063 .Short, 2064 .Int, 2065 .Long, 2066 .LongLong, 2067 .UInt128, 2068 .Int128, 2069 .Float, 2070 .Double, 2071 .Float128, 2072 .LongDouble, 2073 .WChar_U, 2074 .Char8, 2075 .Char16, 2076 .Char32, 2077 .WChar_S, 2078 .Float16, 2079 => { 2080 // node != 0 2081 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); 2082 }, 2083 .NullPtr => { 2084 // node == null 2085 return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); 2086 }, 2087 else => {}, 2088 } 2089 }, 2090 .Pointer => { 2091 if (node.tag() == .string_literal) { 2092 // @intFromPtr(node) != 0 2093 const int_from_ptr = try Tag.int_from_ptr.create(c.arena, node); 2094 return Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() }); 2095 } 2096 // node != null 2097 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); 2098 }, 2099 .Typedef => { 2100 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 2101 const typedef_decl = typedef_ty.getDecl(); 2102 const underlying_type = typedef_decl.getUnderlyingType(); 2103 return finishBoolExpr(c, scope, loc, underlying_type.getTypePtr(), node, used); 2104 }, 2105 .Enum => { 2106 // node != 0 2107 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); 2108 }, 2109 .Elaborated => { 2110 const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty)); 2111 const named_type = elaborated_ty.getNamedType(); 2112 return finishBoolExpr(c, scope, loc, named_type.getTypePtr(), node, used); 2113 }, 2114 else => {}, 2115 } 2116 return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{}); 2117 } 2118 2119 const SuppressCast = enum { 2120 with_as, 2121 no_as, 2122 }; 2123 fn transIntegerLiteral( 2124 c: *Context, 2125 scope: *Scope, 2126 expr: *const clang.IntegerLiteral, 2127 result_used: ResultUsed, 2128 suppress_as: SuppressCast, 2129 ) TransError!Node { 2130 var eval_result: clang.ExprEvalResult = undefined; 2131 if (!expr.EvaluateAsInt(&eval_result, c.clang_context)) { 2132 const loc = expr.getBeginLoc(); 2133 return fail(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); 2134 } 2135 2136 if (suppress_as == .no_as) { 2137 const int_lit_node = try transCreateNodeAPInt(c, eval_result.Val.getInt()); 2138 return maybeSuppressResult(c, result_used, int_lit_node); 2139 } 2140 2141 // Integer literals in C have types, and this can matter for several reasons. 2142 // For example, this is valid C: 2143 // unsigned char y = 256; 2144 // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted 2145 // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code: 2146 // var y = @as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 256))))); 2147 // Ideally in translate-c we could flatten this out to simply: 2148 // var y: u8 = 0; 2149 // But the first step is to be correct, and the next step is to make the output more elegant. 2150 2151 // @as(T, x) 2152 const expr_base = @as(*const clang.Expr, @ptrCast(expr)); 2153 const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()); 2154 const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt()); 2155 const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs }); 2156 return maybeSuppressResult(c, result_used, as); 2157 } 2158 2159 fn transReturnStmt( 2160 c: *Context, 2161 scope: *Scope, 2162 expr: *const clang.ReturnStmt, 2163 ) TransError!Node { 2164 const val_expr = expr.getRetValue() orelse 2165 return Tag.return_void.init(); 2166 2167 var rhs = try transExprCoercing(c, scope, val_expr, .used); 2168 const return_qt = scope.findBlockReturnType(); 2169 if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) { 2170 rhs = try Tag.int_from_bool.create(c.arena, rhs); 2171 } 2172 return Tag.@"return".create(c.arena, rhs); 2173 } 2174 2175 fn transNarrowStringLiteral( 2176 c: *Context, 2177 stmt: *const clang.StringLiteral, 2178 result_used: ResultUsed, 2179 ) TransError!Node { 2180 var len: usize = undefined; 2181 const bytes_ptr = stmt.getString_bytes_begin_size(&len); 2182 2183 const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); 2184 const node = try Tag.string_literal.create(c.arena, str); 2185 return maybeSuppressResult(c, result_used, node); 2186 } 2187 2188 fn transStringLiteral( 2189 c: *Context, 2190 scope: *Scope, 2191 stmt: *const clang.StringLiteral, 2192 result_used: ResultUsed, 2193 ) TransError!Node { 2194 const kind = stmt.getKind(); 2195 switch (kind) { 2196 .Ascii, .UTF8 => return transNarrowStringLiteral(c, stmt, result_used), 2197 .UTF16, .UTF32, .Wide => { 2198 const str_type = @tagName(stmt.getKind()); 2199 const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); 2200 2201 const expr_base = @as(*const clang.Expr, @ptrCast(stmt)); 2202 const array_type = try transQualTypeInitialized(c, scope, expr_base.getType(), expr_base, expr_base.getBeginLoc()); 2203 const lit_array = try transStringLiteralInitializer(c, stmt, array_type); 2204 const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); 2205 try scope.appendNode(decl); 2206 const node = try Tag.identifier.create(c.arena, name); 2207 return maybeSuppressResult(c, result_used, node); 2208 }, 2209 } 2210 } 2211 2212 fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo { 2213 return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data; 2214 } 2215 2216 /// Translate a string literal that is initializing an array. In general narrow string 2217 /// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation. 2218 /// Wide string literals become an array of integers. zero-fillers pad out the array to 2219 /// the appropriate length, if necessary. 2220 fn transStringLiteralInitializer( 2221 c: *Context, 2222 stmt: *const clang.StringLiteral, 2223 array_type: Node, 2224 ) TransError!Node { 2225 assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type); 2226 2227 const is_narrow = stmt.getKind() == .Ascii or stmt.getKind() == .UTF8; 2228 2229 const str_length = stmt.getLength(); 2230 const payload = getArrayPayload(array_type); 2231 const array_size = payload.len; 2232 const elem_type = payload.elem_type; 2233 2234 if (array_size == 0) return Tag.empty_array.create(c.arena, elem_type); 2235 2236 const num_inits = @min(str_length, array_size); 2237 const init_node = if (num_inits > 0) blk: { 2238 if (is_narrow) { 2239 // "string literal".* or string literal"[0..num_inits].* 2240 var str = try transNarrowStringLiteral(c, stmt, .used); 2241 if (str_length != array_size) str = try Tag.string_slice.create(c.arena, .{ .string = str, .end = num_inits }); 2242 break :blk try Tag.deref.create(c.arena, str); 2243 } else { 2244 const init_list = try c.arena.alloc(Node, num_inits); 2245 var i: c_uint = 0; 2246 while (i < num_inits) : (i += 1) { 2247 init_list[i] = try transCreateCharLitNode(c, false, stmt.getCodeUnit(i)); 2248 } 2249 const init_args = .{ .len = num_inits, .elem_type = elem_type }; 2250 const init_array_type = try if (array_type.tag() == .array_type) Tag.array_type.create(c.arena, init_args) else Tag.null_sentinel_array_type.create(c.arena, init_args); 2251 break :blk try Tag.array_init.create(c.arena, .{ 2252 .cond = init_array_type, 2253 .cases = init_list, 2254 }); 2255 } 2256 } else null; 2257 2258 if (num_inits == array_size) return init_node.?; // init_node is only null if num_inits == 0; but if num_inits == array_size == 0 we've already returned 2259 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned. 2260 2261 const filler_node = try Tag.array_filler.create(c.arena, .{ 2262 .type = elem_type, 2263 .filler = Tag.zero_literal.init(), 2264 .count = array_size - str_length, 2265 }); 2266 2267 if (init_node) |some| { 2268 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node }); 2269 } else { 2270 return filler_node; 2271 } 2272 } 2273 2274 /// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where 2275 /// both operands resolve to addresses. The C standard requires that both operands 2276 /// point to elements of the same array object, but we do not verify that here. 2277 fn cIsPointerDiffExpr(stmt: *const clang.BinaryOperator) bool { 2278 const lhs = @as(*const clang.Stmt, @ptrCast(stmt.getLHS())); 2279 const rhs = @as(*const clang.Stmt, @ptrCast(stmt.getRHS())); 2280 return stmt.getOpcode() == .Sub and 2281 qualTypeIsPtr(@as(*const clang.Expr, @ptrCast(lhs)).getType()) and 2282 qualTypeIsPtr(@as(*const clang.Expr, @ptrCast(rhs)).getType()); 2283 } 2284 2285 fn cIsEnum(qt: clang.QualType) bool { 2286 return qt.getCanonicalType().getTypeClass() == .Enum; 2287 } 2288 2289 fn cIsVector(qt: clang.QualType) bool { 2290 return qt.getCanonicalType().getTypeClass() == .Vector; 2291 } 2292 2293 /// Get the underlying int type of an enum. The C compiler chooses a signed int 2294 /// type that is large enough to hold all of the enum's values. It is not required 2295 /// to be the smallest possible type that can hold all the values. 2296 fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType { 2297 assert(cIsEnum(enum_qt)); 2298 const ty = enum_qt.getCanonicalType().getTypePtr(); 2299 const enum_ty = @as(*const clang.EnumType, @ptrCast(ty)); 2300 const enum_decl = enum_ty.getDecl(); 2301 return enum_decl.getIntegerType(); 2302 } 2303 2304 // when modifying this function, make sure to also update std.zig.c_translation.cast 2305 fn transCCast( 2306 c: *Context, 2307 scope: *Scope, 2308 loc: clang.SourceLocation, 2309 dst_type: clang.QualType, 2310 src_type: clang.QualType, 2311 expr: Node, 2312 ) !Node { 2313 if (qualTypeCanon(dst_type).isVoidType()) return expr; 2314 if (dst_type.eq(src_type)) return expr; 2315 if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) 2316 return transCPtrCast(c, scope, loc, dst_type, src_type, expr); 2317 if (cIsEnum(dst_type)) return transCCast(c, scope, loc, cIntTypeForEnum(dst_type), src_type, expr); 2318 if (cIsEnum(src_type)) return transCCast(c, scope, loc, dst_type, cIntTypeForEnum(src_type), expr); 2319 2320 const dst_node = try transQualType(c, scope, dst_type, loc); 2321 if (cIsInteger(dst_type) and cIsInteger(src_type)) { 2322 // 1. If src_type is an enum, determine the underlying signed int type 2323 // 2. Extend or truncate without changing signed-ness. 2324 // 3. Bit-cast to correct signed-ness 2325 const src_type_is_signed = cIsSignedInteger(src_type); 2326 var src_int_expr = expr; 2327 2328 if (isBoolRes(src_int_expr)) { 2329 src_int_expr = try Tag.int_from_bool.create(c.arena, src_int_expr); 2330 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr }); 2331 } 2332 2333 switch (cIntTypeCmp(dst_type, src_type)) { 2334 .lt => { 2335 // @truncate(SameSignSmallerInt, src_int_expr) 2336 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); 2337 src_int_expr = try Tag.as.create(c.arena, .{ 2338 .lhs = ty_node, 2339 .rhs = try Tag.truncate.create(c.arena, src_int_expr), 2340 }); 2341 }, 2342 .gt => { 2343 // @as(SameSignBiggerInt, src_int_expr) 2344 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); 2345 src_int_expr = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); 2346 }, 2347 .eq => { 2348 // src_int_expr = src_int_expr 2349 }, 2350 } 2351 // @as(dest_type, @bitCast(intermediate_value)) 2352 return Tag.as.create(c.arena, .{ 2353 .lhs = dst_node, 2354 .rhs = try Tag.bit_cast.create(c.arena, src_int_expr), 2355 }); 2356 } 2357 if (cIsVector(src_type) or cIsVector(dst_type)) { 2358 // C cast where at least 1 operand is a vector requires them to be same size 2359 // @as(dest_type, @bitCast(val)) 2360 return Tag.as.create(c.arena, .{ 2361 .lhs = dst_node, 2362 .rhs = try Tag.bit_cast.create(c.arena, expr), 2363 }); 2364 } 2365 if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { 2366 // @intCast(dest_type, @intFromPtr(val)) 2367 const int_from_ptr = try Tag.int_from_ptr.create(c.arena, expr); 2368 return Tag.as.create(c.arena, .{ 2369 .lhs = dst_node, 2370 .rhs = try Tag.int_cast.create(c.arena, int_from_ptr), 2371 }); 2372 } 2373 if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) { 2374 // @as(dest_type, @ptrFromInt(val)) 2375 return Tag.as.create(c.arena, .{ 2376 .lhs = dst_node, 2377 .rhs = try Tag.ptr_from_int.create(c.arena, expr), 2378 }); 2379 } 2380 if (cIsFloating(src_type) and cIsFloating(dst_type)) { 2381 // @as(dest_type, @floatCast(val)) 2382 return Tag.as.create(c.arena, .{ 2383 .lhs = dst_node, 2384 .rhs = try Tag.float_cast.create(c.arena, expr), 2385 }); 2386 } 2387 if (cIsFloating(src_type) and !cIsFloating(dst_type)) { 2388 // bool expression: floating val != 0 2389 if (qualTypeIsBoolean(dst_type)) { 2390 return Tag.not_equal.create(c.arena, .{ 2391 .lhs = expr, 2392 .rhs = Tag.zero_literal.init(), 2393 }); 2394 } 2395 2396 // @as(dest_type, @intFromFloat(val)) 2397 return Tag.as.create(c.arena, .{ 2398 .lhs = dst_node, 2399 .rhs = try Tag.int_from_float.create(c.arena, expr), 2400 }); 2401 } 2402 if (!cIsFloating(src_type) and cIsFloating(dst_type)) { 2403 var rhs = expr; 2404 if (qualTypeIsBoolean(src_type) or isBoolRes(rhs)) rhs = try Tag.int_from_bool.create(c.arena, expr); 2405 // @as(dest_type, @floatFromInt(val)) 2406 return Tag.as.create(c.arena, .{ 2407 .lhs = dst_node, 2408 .rhs = try Tag.float_from_int.create(c.arena, rhs), 2409 }); 2410 } 2411 if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) { 2412 // @intFromBool returns a u1 2413 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast 2414 // instead of @as 2415 const int_from_bool = try Tag.int_from_bool.create(c.arena, expr); 2416 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = int_from_bool }); 2417 } 2418 // @as(dest_type, val) 2419 return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); 2420 } 2421 2422 fn transExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { 2423 return transStmt(c, scope, @as(*const clang.Stmt, @ptrCast(expr)), used); 2424 } 2425 2426 /// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore 2427 /// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. 2428 fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { 2429 switch (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass()) { 2430 .IntegerLiteralClass => { 2431 return transIntegerLiteral(c, scope, @as(*const clang.IntegerLiteral, @ptrCast(expr)), .used, .no_as); 2432 }, 2433 .CharacterLiteralClass => { 2434 return transCharLiteral(c, scope, @as(*const clang.CharacterLiteral, @ptrCast(expr)), .used, .no_as); 2435 }, 2436 .UnaryOperatorClass => { 2437 const un_expr = @as(*const clang.UnaryOperator, @ptrCast(expr)); 2438 if (un_expr.getOpcode() == .Extension) { 2439 return transExprCoercing(c, scope, un_expr.getSubExpr(), used); 2440 } 2441 }, 2442 .ImplicitCastExprClass => { 2443 const cast_expr = @as(*const clang.ImplicitCastExpr, @ptrCast(expr)); 2444 const sub_expr = cast_expr.getSubExpr(); 2445 switch (@as(*const clang.Stmt, @ptrCast(sub_expr)).getStmtClass()) { 2446 .IntegerLiteralClass, .CharacterLiteralClass => switch (cast_expr.getCastKind()) { 2447 .IntegralToFloating => return transExprCoercing(c, scope, sub_expr, used), 2448 .IntegralCast => { 2449 const dest_type = getExprQualType(c, expr); 2450 if (literalFitsInType(c, sub_expr, dest_type)) 2451 return transExprCoercing(c, scope, sub_expr, used); 2452 }, 2453 else => {}, 2454 }, 2455 else => {}, 2456 } 2457 }, 2458 else => {}, 2459 } 2460 return transExpr(c, scope, expr, .used); 2461 } 2462 2463 fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) bool { 2464 var width = qualTypeIntBitWidth(c, qt) catch 8; 2465 if (width == 0) width = 8; // Byte is the smallest type. 2466 const is_signed = cIsSignedInteger(qt); 2467 const width_max_int = (@as(u64, 1) << math.lossyCast(u6, width - @intFromBool(is_signed))) - 1; 2468 2469 switch (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass()) { 2470 .CharacterLiteralClass => { 2471 const char_lit = @as(*const clang.CharacterLiteral, @ptrCast(expr)); 2472 const val = char_lit.getValue(); 2473 // If the val is less than the max int then it fits. 2474 return val <= width_max_int; 2475 }, 2476 .IntegerLiteralClass => { 2477 const int_lit = @as(*const clang.IntegerLiteral, @ptrCast(expr)); 2478 var eval_result: clang.ExprEvalResult = undefined; 2479 if (!int_lit.EvaluateAsInt(&eval_result, c.clang_context)) { 2480 return false; 2481 } 2482 2483 const int = eval_result.Val.getInt(); 2484 return int.lessThanEqual(width_max_int); 2485 }, 2486 else => unreachable, 2487 } 2488 } 2489 2490 fn transInitListExprRecord( 2491 c: *Context, 2492 scope: *Scope, 2493 loc: clang.SourceLocation, 2494 expr: *const clang.InitListExpr, 2495 ty: *const clang.Type, 2496 ) TransError!Node { 2497 var is_union_type = false; 2498 // Unions and Structs are both represented as RecordDecl 2499 const record_ty = ty.getAsRecordType() orelse 2500 blk: { 2501 is_union_type = true; 2502 break :blk ty.getAsUnionType(); 2503 } orelse unreachable; 2504 const record_decl = record_ty.getDecl(); 2505 const record_def = record_decl.getDefinition() orelse 2506 unreachable; 2507 2508 const ty_node = try transType(c, scope, ty, loc); 2509 const init_count = expr.getNumInits(); 2510 var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa); 2511 defer field_inits.deinit(); 2512 2513 if (init_count == 0) { 2514 const source_loc = @as(*const clang.Expr, @ptrCast(expr)).getBeginLoc(); 2515 return transZeroInitExpr(c, scope, source_loc, ty); 2516 } 2517 2518 var init_i: c_uint = 0; 2519 var it = record_def.field_begin(); 2520 const end_it = record_def.field_end(); 2521 while (it.neq(end_it)) : (it = it.next()) { 2522 const field_decl = it.deref(); 2523 2524 // The initializer for a union type has a single entry only 2525 if (is_union_type and field_decl != expr.getInitializedFieldInUnion()) { 2526 continue; 2527 } 2528 2529 assert(init_i < init_count); 2530 const elem_expr = expr.getInit(init_i); 2531 init_i += 1; 2532 2533 // Generate the field assignment expression: 2534 // .field_name = expr 2535 var raw_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin()); 2536 if (field_decl.isAnonymousStructOrUnion()) { 2537 const name = c.decl_table.get(@intFromPtr(field_decl.getCanonicalDecl())).?; 2538 raw_name = try c.arena.dupe(u8, name); 2539 } 2540 2541 var init_expr = try transExpr(c, scope, elem_expr, .used); 2542 const field_qt = field_decl.getType(); 2543 if (init_expr.tag() == .string_literal and qualTypeIsCharStar(field_qt)) { 2544 if (scope.id == .root) { 2545 init_expr = try stringLiteralToCharStar(c, init_expr); 2546 } else { 2547 const dst_type_node = try transQualType(c, scope, field_qt, loc); 2548 init_expr = try removeCVQualifiers(c, dst_type_node, init_expr); 2549 } 2550 } 2551 try field_inits.append(.{ 2552 .name = raw_name, 2553 .value = init_expr, 2554 }); 2555 } 2556 if (ty_node.castTag(.identifier)) |ident_node| { 2557 scope.skipVariableDiscard(ident_node.data); 2558 } 2559 return Tag.container_init.create(c.arena, .{ 2560 .lhs = ty_node, 2561 .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items), 2562 }); 2563 } 2564 2565 fn transInitListExprArray( 2566 c: *Context, 2567 scope: *Scope, 2568 loc: clang.SourceLocation, 2569 expr: *const clang.InitListExpr, 2570 ty: *const clang.Type, 2571 ) TransError!Node { 2572 const arr_type = ty.getAsArrayTypeUnsafe(); 2573 const child_qt = arr_type.getElementType(); 2574 const child_type = try transQualType(c, scope, child_qt, loc); 2575 const init_count = expr.getNumInits(); 2576 assert(@as(*const clang.Type, @ptrCast(arr_type)).isConstantArrayType()); 2577 const const_arr_ty = @as(*const clang.ConstantArrayType, @ptrCast(arr_type)); 2578 const size_ap_int = const_arr_ty.getSize(); 2579 const all_count = size_ap_int.getLimitedValue(usize); 2580 const leftover_count = all_count - init_count; 2581 2582 if (all_count == 0) { 2583 return Tag.empty_array.create(c.arena, child_type); 2584 } 2585 2586 if (expr.isStringLiteralInit()) { 2587 assert(init_count == 1); 2588 const init_expr = expr.getInit(0); 2589 const string_literal = init_expr.castToStringLiteral().?; 2590 return try transStringLiteral(c, scope, string_literal, .used); 2591 } 2592 2593 const init_node = if (init_count != 0) blk: { 2594 const init_list = try c.arena.alloc(Node, init_count); 2595 2596 for (init_list, 0..) |*init, i| { 2597 const elem_expr = expr.getInit(@as(c_uint, @intCast(i))); 2598 init.* = try transExprCoercing(c, scope, elem_expr, .used); 2599 } 2600 const init_node = try Tag.array_init.create(c.arena, .{ 2601 .cond = try Tag.array_type.create(c.arena, .{ .len = init_count, .elem_type = child_type }), 2602 .cases = init_list, 2603 }); 2604 if (leftover_count == 0) { 2605 return init_node; 2606 } 2607 break :blk init_node; 2608 } else null; 2609 2610 assert(expr.hasArrayFiller()); 2611 const filler_val_expr = expr.getArrayFiller(); 2612 const filler_node = try Tag.array_filler.create(c.arena, .{ 2613 .type = child_type, 2614 .filler = try transExprCoercing(c, scope, filler_val_expr, .used), 2615 .count = leftover_count, 2616 }); 2617 2618 if (init_node) |some| { 2619 return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node }); 2620 } else { 2621 return filler_node; 2622 } 2623 } 2624 2625 fn transInitListExprVector( 2626 c: *Context, 2627 scope: *Scope, 2628 loc: clang.SourceLocation, 2629 expr: *const clang.InitListExpr, 2630 ) TransError!Node { 2631 const qt = getExprQualType(c, @as(*const clang.Expr, @ptrCast(expr))); 2632 const vector_ty = @as(*const clang.VectorType, @ptrCast(qualTypeCanon(qt))); 2633 2634 const init_count = expr.getNumInits(); 2635 const num_elements = vector_ty.getNumElements(); 2636 const element_qt = vector_ty.getElementType(); 2637 2638 if (init_count == 0) { 2639 const vec_node = try Tag.vector.create(c.arena, .{ 2640 .lhs = try transCreateNodeNumber(c, num_elements, .int), 2641 .rhs = try transQualType(c, scope, element_qt, loc), 2642 }); 2643 2644 return Tag.as.create(c.arena, .{ 2645 .lhs = vec_node, 2646 .rhs = try Tag.vector_zero_init.create(c.arena, Tag.zero_literal.init()), 2647 }); 2648 } 2649 2650 const vector_type = try transQualType(c, scope, qt, loc); 2651 2652 var block_scope = try Scope.Block.init(c, scope, true); 2653 defer block_scope.deinit(); 2654 2655 // workaround for https://github.com/ziglang/zig/issues/8322 2656 // we store the initializers in temp variables and use those 2657 // to initialize the vector. Eventually we can just directly 2658 // construct the init_list from casted source members 2659 var i: usize = 0; 2660 while (i < init_count) : (i += 1) { 2661 const mangled_name = try block_scope.makeMangledName(c, "tmp"); 2662 const init_expr = expr.getInit(@as(c_uint, @intCast(i))); 2663 const tmp_decl_node = try Tag.var_simple.create(c.arena, .{ 2664 .name = mangled_name, 2665 .init = try transExpr(c, &block_scope.base, init_expr, .used), 2666 }); 2667 try block_scope.statements.append(tmp_decl_node); 2668 } 2669 2670 const init_list = try c.arena.alloc(Node, num_elements); 2671 for (init_list, 0..) |*init, init_index| { 2672 if (init_index < init_count) { 2673 const tmp_decl = block_scope.statements.items[init_index]; 2674 const name = tmp_decl.castTag(.var_simple).?.data.name; 2675 init.* = try Tag.identifier.create(c.arena, name); 2676 } else { 2677 init.* = Tag.undefined_literal.init(); 2678 } 2679 } 2680 2681 const array_init = try Tag.array_init.create(c.arena, .{ 2682 .cond = vector_type, 2683 .cases = init_list, 2684 }); 2685 const break_node = try Tag.break_val.create(c.arena, .{ 2686 .label = block_scope.label, 2687 .val = array_init, 2688 }); 2689 try block_scope.statements.append(break_node); 2690 2691 return block_scope.complete(c); 2692 } 2693 2694 fn transInitListExpr( 2695 c: *Context, 2696 scope: *Scope, 2697 expr: *const clang.InitListExpr, 2698 used: ResultUsed, 2699 ) TransError!Node { 2700 const qt = getExprQualType(c, @as(*const clang.Expr, @ptrCast(expr))); 2701 var qual_type = qt.getTypePtr(); 2702 const source_loc = @as(*const clang.Expr, @ptrCast(expr)).getBeginLoc(); 2703 2704 if (qualTypeWasDemotedToOpaque(c, qt)) { 2705 return fail(c, error.UnsupportedTranslation, source_loc, "cannot initialize opaque type", .{}); 2706 } 2707 2708 if (qual_type.isRecordType()) { 2709 return maybeSuppressResult(c, used, try transInitListExprRecord( 2710 c, 2711 scope, 2712 source_loc, 2713 expr, 2714 qual_type, 2715 )); 2716 } else if (qual_type.isArrayType()) { 2717 return maybeSuppressResult(c, used, try transInitListExprArray( 2718 c, 2719 scope, 2720 source_loc, 2721 expr, 2722 qual_type, 2723 )); 2724 } else if (qual_type.isVectorType()) { 2725 return maybeSuppressResult(c, used, try transInitListExprVector(c, scope, source_loc, expr)); 2726 } else { 2727 const type_name = try c.str(qual_type.getTypeClassName()); 2728 return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); 2729 } 2730 } 2731 2732 fn transZeroInitExpr( 2733 c: *Context, 2734 scope: *Scope, 2735 source_loc: clang.SourceLocation, 2736 ty: *const clang.Type, 2737 ) TransError!Node { 2738 switch (ty.getTypeClass()) { 2739 .Builtin => { 2740 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 2741 switch (builtin_ty.getKind()) { 2742 .Bool => return Tag.false_literal.init(), 2743 .Char_U, 2744 .UChar, 2745 .Char_S, 2746 .Char8, 2747 .SChar, 2748 .UShort, 2749 .UInt, 2750 .ULong, 2751 .ULongLong, 2752 .Short, 2753 .Int, 2754 .Long, 2755 .LongLong, 2756 .UInt128, 2757 .Int128, 2758 .Float, 2759 .Double, 2760 .Float128, 2761 .Float16, 2762 .LongDouble, 2763 => return Tag.zero_literal.init(), 2764 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), 2765 } 2766 }, 2767 .Pointer => return Tag.null_literal.init(), 2768 .Typedef => { 2769 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 2770 const typedef_decl = typedef_ty.getDecl(); 2771 return transZeroInitExpr( 2772 c, 2773 scope, 2774 source_loc, 2775 typedef_decl.getUnderlyingType().getTypePtr(), 2776 ); 2777 }, 2778 else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)), 2779 } 2780 } 2781 2782 fn transImplicitValueInitExpr( 2783 c: *Context, 2784 scope: *Scope, 2785 expr: *const clang.Expr, 2786 ) TransError!Node { 2787 const source_loc = expr.getBeginLoc(); 2788 const qt = getExprQualType(c, expr); 2789 const ty = qt.getTypePtr(); 2790 return transZeroInitExpr(c, scope, source_loc, ty); 2791 } 2792 2793 /// If a statement can possibly translate to a Zig assignment (either directly because it's 2794 /// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement 2795 /// in the body of an if statement or loop, then we need to put the statement into its own block. 2796 /// The `else` case here corresponds to statements that could result in an assignment. If a statement 2797 /// class never needs a block, add its enum to the top prong. 2798 fn maybeBlockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node { 2799 switch (stmt.getStmtClass()) { 2800 .BreakStmtClass, 2801 .CompoundStmtClass, 2802 .ContinueStmtClass, 2803 .DeclRefExprClass, 2804 .DeclStmtClass, 2805 .DoStmtClass, 2806 .ForStmtClass, 2807 .IfStmtClass, 2808 .ReturnStmtClass, 2809 .NullStmtClass, 2810 .WhileStmtClass, 2811 => return transStmt(c, scope, stmt, .unused), 2812 else => return blockify(c, scope, stmt), 2813 } 2814 } 2815 2816 fn blockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node { 2817 var block_scope = try Scope.Block.init(c, scope, false); 2818 defer block_scope.deinit(); 2819 const result = try transStmt(c, &block_scope.base, stmt, .unused); 2820 try block_scope.statements.append(result); 2821 return block_scope.complete(c); 2822 } 2823 2824 fn transIfStmt( 2825 c: *Context, 2826 scope: *Scope, 2827 stmt: *const clang.IfStmt, 2828 ) TransError!Node { 2829 // if (c) t 2830 // if (c) t else e 2831 var cond_scope = Scope.Condition{ 2832 .base = .{ 2833 .parent = scope, 2834 .id = .condition, 2835 }, 2836 }; 2837 defer cond_scope.deinit(); 2838 const cond_expr = @as(*const clang.Expr, @ptrCast(stmt.getCond())); 2839 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); 2840 2841 const then_stmt = stmt.getThen(); 2842 const else_stmt = stmt.getElse(); 2843 const then_class = then_stmt.getStmtClass(); 2844 // block needed to keep else statement from attaching to inner while 2845 const must_blockify = (else_stmt != null) and switch (then_class) { 2846 .DoStmtClass, .ForStmtClass, .WhileStmtClass => true, 2847 else => false, 2848 }; 2849 2850 const then_body = if (must_blockify) 2851 try blockify(c, scope, then_stmt) 2852 else 2853 try maybeBlockify(c, scope, then_stmt); 2854 2855 const else_body = if (else_stmt) |expr| 2856 try maybeBlockify(c, scope, expr) 2857 else 2858 null; 2859 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); 2860 } 2861 2862 fn transWhileLoop( 2863 c: *Context, 2864 scope: *Scope, 2865 stmt: *const clang.WhileStmt, 2866 ) TransError!Node { 2867 var cond_scope = Scope.Condition{ 2868 .base = .{ 2869 .parent = scope, 2870 .id = .condition, 2871 }, 2872 }; 2873 defer cond_scope.deinit(); 2874 const cond_expr = @as(*const clang.Expr, @ptrCast(stmt.getCond())); 2875 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); 2876 2877 var loop_scope = Scope{ 2878 .parent = scope, 2879 .id = .loop, 2880 }; 2881 const body = try maybeBlockify(c, &loop_scope, stmt.getBody()); 2882 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); 2883 } 2884 2885 fn transDoWhileLoop( 2886 c: *Context, 2887 scope: *Scope, 2888 stmt: *const clang.DoStmt, 2889 ) TransError!Node { 2890 var loop_scope = Scope{ 2891 .parent = scope, 2892 .id = .do_loop, 2893 }; 2894 2895 // if (!cond) break; 2896 var cond_scope = Scope.Condition{ 2897 .base = .{ 2898 .parent = scope, 2899 .id = .condition, 2900 }, 2901 }; 2902 defer cond_scope.deinit(); 2903 const cond = try transBoolExpr(c, &cond_scope.base, @as(*const clang.Expr, @ptrCast(stmt.getCond())), .used); 2904 const if_not_break = switch (cond.tag()) { 2905 .true_literal => { 2906 const body_node = try maybeBlockify(c, scope, stmt.getBody()); 2907 return Tag.while_true.create(c.arena, body_node); 2908 }, 2909 else => try Tag.if_not_break.create(c.arena, cond), 2910 }; 2911 2912 var body_node = try transStmt(c, &loop_scope, stmt.getBody(), .unused); 2913 if (body_node.isNoreturn(true)) { 2914 // The body node ends in a noreturn statement. Simply put it in a while (true) 2915 // in case it contains breaks or continues. 2916 } else if (stmt.getBody().getStmtClass() == .CompoundStmtClass) { 2917 // there's already a block in C, so we'll append our condition to it. 2918 // c: do { 2919 // c: a; 2920 // c: b; 2921 // c: } while(c); 2922 // zig: while (true) { 2923 // zig: a; 2924 // zig: b; 2925 // zig: if (!cond) break; 2926 // zig: } 2927 const block = body_node.castTag(.block).?; 2928 block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete. 2929 block.data.stmts[block.data.stmts.len - 1] = if_not_break; 2930 } else { 2931 // the C statement is without a block, so we need to create a block to contain it. 2932 // c: do 2933 // c: a; 2934 // c: while(c); 2935 // zig: while (true) { 2936 // zig: a; 2937 // zig: if (!cond) break; 2938 // zig: } 2939 const statements = try c.arena.alloc(Node, 2); 2940 statements[0] = body_node; 2941 statements[1] = if_not_break; 2942 body_node = try Tag.block.create(c.arena, .{ .label = null, .stmts = statements }); 2943 } 2944 return Tag.while_true.create(c.arena, body_node); 2945 } 2946 2947 fn transForLoop( 2948 c: *Context, 2949 scope: *Scope, 2950 stmt: *const clang.ForStmt, 2951 ) TransError!Node { 2952 var loop_scope = Scope{ 2953 .parent = scope, 2954 .id = .loop, 2955 }; 2956 2957 var block_scope: ?Scope.Block = null; 2958 defer if (block_scope) |*bs| bs.deinit(); 2959 2960 if (stmt.getInit()) |init| { 2961 block_scope = try Scope.Block.init(c, scope, false); 2962 loop_scope.parent = &block_scope.?.base; 2963 const init_node = try transStmt(c, &block_scope.?.base, init, .unused); 2964 if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node); 2965 } 2966 var cond_scope = Scope.Condition{ 2967 .base = .{ 2968 .parent = &loop_scope, 2969 .id = .condition, 2970 }, 2971 }; 2972 defer cond_scope.deinit(); 2973 2974 const cond = if (stmt.getCond()) |cond| 2975 try transBoolExpr(c, &cond_scope.base, cond, .used) 2976 else 2977 Tag.true_literal.init(); 2978 2979 const cont_expr = if (stmt.getInc()) |incr| 2980 try transExpr(c, &cond_scope.base, incr, .unused) 2981 else 2982 null; 2983 2984 const body = try maybeBlockify(c, &loop_scope, stmt.getBody()); 2985 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); 2986 if (block_scope) |*bs| { 2987 try bs.statements.append(while_node); 2988 return try bs.complete(c); 2989 } else { 2990 return while_node; 2991 } 2992 } 2993 2994 fn transSwitch( 2995 c: *Context, 2996 scope: *Scope, 2997 stmt: *const clang.SwitchStmt, 2998 ) TransError!Node { 2999 var loop_scope = Scope{ 3000 .parent = scope, 3001 .id = .loop, 3002 }; 3003 3004 var block_scope = try Scope.Block.init(c, &loop_scope, false); 3005 defer block_scope.deinit(); 3006 3007 const base_scope = &block_scope.base; 3008 3009 var cond_scope = Scope.Condition{ 3010 .base = .{ 3011 .parent = base_scope, 3012 .id = .condition, 3013 }, 3014 }; 3015 defer cond_scope.deinit(); 3016 const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used); 3017 3018 var cases = std.ArrayList(Node).init(c.gpa); 3019 defer cases.deinit(); 3020 var has_default = false; 3021 3022 const body = stmt.getBody(); 3023 assert(body.getStmtClass() == .CompoundStmtClass); 3024 const compound_stmt = @as(*const clang.CompoundStmt, @ptrCast(body)); 3025 var it = compound_stmt.body_begin(); 3026 const end_it = compound_stmt.body_end(); 3027 // Iterate over switch body and collect all cases. 3028 // Fallthrough is handled by duplicating statements. 3029 while (it != end_it) : (it += 1) { 3030 switch (it[0].getStmtClass()) { 3031 .CaseStmtClass => { 3032 var items = std.ArrayList(Node).init(c.gpa); 3033 defer items.deinit(); 3034 const sub = try transCaseStmt(c, base_scope, it[0], &items); 3035 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it); 3036 3037 if (items.items.len == 0) { 3038 has_default = true; 3039 const switch_else = try Tag.switch_else.create(c.arena, res); 3040 try cases.append(switch_else); 3041 } else { 3042 const switch_prong = try Tag.switch_prong.create(c.arena, .{ 3043 .cases = try c.arena.dupe(Node, items.items), 3044 .cond = res, 3045 }); 3046 try cases.append(switch_prong); 3047 } 3048 }, 3049 .DefaultStmtClass => { 3050 has_default = true; 3051 const default_stmt = @as(*const clang.DefaultStmt, @ptrCast(it[0])); 3052 3053 var sub = default_stmt.getSubStmt(); 3054 while (true) switch (sub.getStmtClass()) { 3055 .CaseStmtClass => sub = @as(*const clang.CaseStmt, @ptrCast(sub)).getSubStmt(), 3056 .DefaultStmtClass => sub = @as(*const clang.DefaultStmt, @ptrCast(sub)).getSubStmt(), 3057 else => break, 3058 }; 3059 3060 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it); 3061 3062 const switch_else = try Tag.switch_else.create(c.arena, res); 3063 try cases.append(switch_else); 3064 }, 3065 else => {}, // collected in transSwitchProngStmt 3066 } 3067 } 3068 3069 if (!has_default) { 3070 const else_prong = try Tag.switch_else.create(c.arena, Tag.empty_block.init()); 3071 try cases.append(else_prong); 3072 } 3073 3074 const switch_node = try Tag.@"switch".create(c.arena, .{ 3075 .cond = switch_expr, 3076 .cases = try c.arena.dupe(Node, cases.items), 3077 }); 3078 try block_scope.statements.append(switch_node); 3079 try block_scope.statements.append(Tag.@"break".init()); 3080 const while_body = try block_scope.complete(c); 3081 3082 return Tag.while_true.create(c.arena, while_body); 3083 } 3084 3085 /// Collects all items for this case, returns the first statement after the labels. 3086 /// If items ends up empty, the prong should be translated as an else. 3087 fn transCaseStmt(c: *Context, scope: *Scope, stmt: *const clang.Stmt, items: *std.ArrayList(Node)) TransError!*const clang.Stmt { 3088 var sub = stmt; 3089 var seen_default = false; 3090 while (true) { 3091 switch (sub.getStmtClass()) { 3092 .DefaultStmtClass => { 3093 seen_default = true; 3094 items.items.len = 0; 3095 const default_stmt = @as(*const clang.DefaultStmt, @ptrCast(sub)); 3096 sub = default_stmt.getSubStmt(); 3097 }, 3098 .CaseStmtClass => { 3099 const case_stmt = @as(*const clang.CaseStmt, @ptrCast(sub)); 3100 3101 if (seen_default) { 3102 items.items.len = 0; 3103 sub = case_stmt.getSubStmt(); 3104 continue; 3105 } 3106 3107 const expr = if (case_stmt.getRHS()) |rhs| blk: { 3108 const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used); 3109 const rhs_node = try transExprCoercing(c, scope, rhs, .used); 3110 3111 break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); 3112 } else try transExprCoercing(c, scope, case_stmt.getLHS(), .used); 3113 3114 try items.append(expr); 3115 sub = case_stmt.getSubStmt(); 3116 }, 3117 else => return sub, 3118 } 3119 } 3120 } 3121 3122 /// Collects all statements seen by this case into a block. 3123 /// Avoids creating a block if the first statement is a break or return. 3124 fn transSwitchProngStmt( 3125 c: *Context, 3126 scope: *Scope, 3127 stmt: *const clang.Stmt, 3128 parent_it: clang.CompoundStmt.ConstBodyIterator, 3129 parent_end_it: clang.CompoundStmt.ConstBodyIterator, 3130 ) TransError!Node { 3131 switch (stmt.getStmtClass()) { 3132 .BreakStmtClass => return Tag.@"break".init(), 3133 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused), 3134 .CaseStmtClass, .DefaultStmtClass => unreachable, 3135 else => { 3136 var block_scope = try Scope.Block.init(c, scope, false); 3137 defer block_scope.deinit(); 3138 3139 // we do not need to translate `stmt` since it is the first stmt of `parent_it` 3140 try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it); 3141 return try block_scope.complete(c); 3142 }, 3143 } 3144 } 3145 3146 /// Collects all statements seen by this case into a block. 3147 fn transSwitchProngStmtInline( 3148 c: *Context, 3149 block: *Scope.Block, 3150 start_it: clang.CompoundStmt.ConstBodyIterator, 3151 end_it: clang.CompoundStmt.ConstBodyIterator, 3152 ) TransError!void { 3153 var it = start_it; 3154 while (it != end_it) : (it += 1) { 3155 switch (it[0].getStmtClass()) { 3156 .ReturnStmtClass => { 3157 const result = try transStmt(c, &block.base, it[0], .unused); 3158 try block.statements.append(result); 3159 return; 3160 }, 3161 .BreakStmtClass => { 3162 try block.statements.append(Tag.@"break".init()); 3163 return; 3164 }, 3165 .CaseStmtClass => { 3166 var sub = @as(*const clang.CaseStmt, @ptrCast(it[0])).getSubStmt(); 3167 while (true) switch (sub.getStmtClass()) { 3168 .CaseStmtClass => sub = @as(*const clang.CaseStmt, @ptrCast(sub)).getSubStmt(), 3169 .DefaultStmtClass => sub = @as(*const clang.DefaultStmt, @ptrCast(sub)).getSubStmt(), 3170 else => break, 3171 }; 3172 const result = try transStmt(c, &block.base, sub, .unused); 3173 assert(result.tag() != .declaration); 3174 try block.statements.append(result); 3175 if (result.isNoreturn(true)) { 3176 return; 3177 } 3178 }, 3179 .DefaultStmtClass => { 3180 var sub = @as(*const clang.DefaultStmt, @ptrCast(it[0])).getSubStmt(); 3181 while (true) switch (sub.getStmtClass()) { 3182 .CaseStmtClass => sub = @as(*const clang.CaseStmt, @ptrCast(sub)).getSubStmt(), 3183 .DefaultStmtClass => sub = @as(*const clang.DefaultStmt, @ptrCast(sub)).getSubStmt(), 3184 else => break, 3185 }; 3186 const result = try transStmt(c, &block.base, sub, .unused); 3187 assert(result.tag() != .declaration); 3188 try block.statements.append(result); 3189 if (result.isNoreturn(true)) { 3190 return; 3191 } 3192 }, 3193 .CompoundStmtClass => { 3194 const result = try transCompoundStmt(c, &block.base, @as(*const clang.CompoundStmt, @ptrCast(it[0]))); 3195 try block.statements.append(result); 3196 if (result.isNoreturn(true)) { 3197 return; 3198 } 3199 }, 3200 else => { 3201 const result = try transStmt(c, &block.base, it[0], .unused); 3202 switch (result.tag()) { 3203 .declaration, .empty_block => {}, 3204 else => try block.statements.append(result), 3205 } 3206 }, 3207 } 3208 } 3209 return; 3210 } 3211 3212 fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { 3213 var result: clang.ExprEvalResult = undefined; 3214 if (!expr.evaluateAsConstantExpr(&result, .Normal, c.clang_context)) 3215 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{}); 3216 3217 switch (result.Val.getKind()) { 3218 .Int => { 3219 // See comment in `transIntegerLiteral` for why this code is here. 3220 // @as(T, x) 3221 const expr_base = @as(*const clang.Expr, @ptrCast(expr)); 3222 const as_node = try Tag.as.create(c.arena, .{ 3223 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()), 3224 .rhs = try transCreateNodeAPInt(c, result.Val.getInt()), 3225 }); 3226 return maybeSuppressResult(c, used, as_node); 3227 }, 3228 else => |kind| { 3229 return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind '{}'", .{kind}); 3230 }, 3231 } 3232 } 3233 3234 fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!Node { 3235 return transStringLiteral(c, scope, expr.getFunctionName(), used); 3236 } 3237 3238 fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { 3239 return Tag.char_literal.create(c.arena, if (narrow) 3240 try std.fmt.allocPrint(c.arena, "'{'}'", .{std.zig.fmtEscapes(&.{@as(u8, @intCast(val))})}) 3241 else 3242 try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val})); 3243 } 3244 3245 fn transCharLiteral( 3246 c: *Context, 3247 scope: *Scope, 3248 stmt: *const clang.CharacterLiteral, 3249 result_used: ResultUsed, 3250 suppress_as: SuppressCast, 3251 ) TransError!Node { 3252 const kind = stmt.getKind(); 3253 const val = stmt.getValue(); 3254 const narrow = kind == .Ascii or kind == .UTF8; 3255 // C has a somewhat obscure feature called multi-character character constant 3256 // e.g. 'abcd' 3257 const int_lit_node = if (kind == .Ascii and val > 255) 3258 try transCreateNodeNumber(c, val, .int) 3259 else 3260 try transCreateCharLitNode(c, narrow, val); 3261 3262 if (suppress_as == .no_as) { 3263 return maybeSuppressResult(c, result_used, int_lit_node); 3264 } 3265 // See comment in `transIntegerLiteral` for why this code is here. 3266 // @as(T, x) 3267 const expr_base = @as(*const clang.Expr, @ptrCast(stmt)); 3268 const as_node = try Tag.as.create(c.arena, .{ 3269 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()), 3270 .rhs = int_lit_node, 3271 }); 3272 return maybeSuppressResult(c, result_used, as_node); 3273 } 3274 3275 fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!Node { 3276 const comp = stmt.getSubStmt(); 3277 if (used == .unused) { 3278 return transCompoundStmt(c, scope, comp); 3279 } 3280 var block_scope = try Scope.Block.init(c, scope, true); 3281 defer block_scope.deinit(); 3282 3283 var it = comp.body_begin(); 3284 const end_it = comp.body_end(); 3285 while (it != end_it - 1) : (it += 1) { 3286 const result = try transStmt(c, &block_scope.base, it[0], .unused); 3287 switch (result.tag()) { 3288 .declaration, .empty_block => {}, 3289 else => try block_scope.statements.append(result), 3290 } 3291 } 3292 3293 const last_result = try transStmt(c, &block_scope.base, it[0], .used); 3294 switch (last_result.tag()) { 3295 .declaration, .empty_block => {}, 3296 else => { 3297 const break_node = try Tag.break_val.create(c.arena, .{ 3298 .label = block_scope.label, 3299 .val = last_result, 3300 }); 3301 try block_scope.statements.append(break_node); 3302 }, 3303 } 3304 const res = try block_scope.complete(c); 3305 return maybeSuppressResult(c, used, res); 3306 } 3307 3308 fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node { 3309 var container_node = try transExpr(c, scope, stmt.getBase(), .used); 3310 if (stmt.isArrow()) { 3311 container_node = try Tag.deref.create(c.arena, container_node); 3312 } 3313 3314 const member_decl = stmt.getMemberDecl(); 3315 const name = blk: { 3316 const decl_kind = @as(*const clang.Decl, @ptrCast(member_decl)).getKind(); 3317 // If we're referring to a anonymous struct/enum find the bogus name 3318 // we've assigned to it during the RecordDecl translation 3319 if (decl_kind == .Field) { 3320 const field_decl = @as(*const clang.FieldDecl, @ptrCast(member_decl)); 3321 if (field_decl.isAnonymousStructOrUnion()) { 3322 const name = c.decl_table.get(@intFromPtr(field_decl.getCanonicalDecl())).?; 3323 break :blk try c.arena.dupe(u8, name); 3324 } 3325 } 3326 const decl = @as(*const clang.NamedDecl, @ptrCast(member_decl)); 3327 break :blk try c.str(decl.getName_bytes_begin()); 3328 }; 3329 3330 var node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name }); 3331 if (exprIsFlexibleArrayRef(c, @as(*const clang.Expr, @ptrCast(stmt)))) { 3332 node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &.{} }); 3333 } 3334 return maybeSuppressResult(c, result_used, node); 3335 } 3336 3337 /// ptr[subscr] (`subscr` is a signed integer expression, `ptr` a pointer) becomes: 3338 /// (blk: { 3339 /// const tmp = subscr; 3340 /// if (tmp >= 0) break :blk ptr + @intCast(usize, tmp) else break :blk ptr - ~@bitCast(usize, @intCast(isize, tmp) +% -1); 3341 /// }).* 3342 /// Todo: rip this out once `[*]T + isize` becomes valid. 3343 fn transSignedArrayAccess( 3344 c: *Context, 3345 scope: *Scope, 3346 container_expr: *const clang.Expr, 3347 subscr_expr: *const clang.Expr, 3348 result_used: ResultUsed, 3349 ) TransError!Node { 3350 var block_scope = try Scope.Block.init(c, scope, true); 3351 defer block_scope.deinit(); 3352 3353 const tmp = try block_scope.makeMangledName(c, "tmp"); 3354 3355 const subscr_node = try transExpr(c, &block_scope.base, subscr_expr, .used); 3356 const subscr_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = subscr_node }); 3357 try block_scope.statements.append(subscr_decl); 3358 3359 const tmp_ref = try Tag.identifier.create(c.arena, tmp); 3360 3361 const container_node = try transExpr(c, &block_scope.base, container_expr, .used); 3362 3363 const cond_node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = tmp_ref, .rhs = Tag.zero_literal.init() }); 3364 3365 const then_value = try Tag.add.create(c.arena, .{ 3366 .lhs = container_node, 3367 .rhs = try Tag.as.create(c.arena, .{ 3368 .lhs = try Tag.type.create(c.arena, "usize"), 3369 .rhs = try Tag.int_cast.create(c.arena, tmp_ref), 3370 }), 3371 }); 3372 3373 const then_body = try Tag.break_val.create(c.arena, .{ 3374 .label = block_scope.label, 3375 .val = then_value, 3376 }); 3377 3378 const minuend = container_node; 3379 const signed_size = try Tag.as.create(c.arena, .{ 3380 .lhs = try Tag.type.create(c.arena, "isize"), 3381 .rhs = try Tag.int_cast.create(c.arena, tmp_ref), 3382 }); 3383 const to_cast = try Tag.add_wrap.create(c.arena, .{ 3384 .lhs = signed_size, 3385 .rhs = try Tag.negate.create(c.arena, Tag.one_literal.init()), 3386 }); 3387 const bitcast_node = try Tag.as.create(c.arena, .{ 3388 .lhs = try Tag.type.create(c.arena, "usize"), 3389 .rhs = try Tag.bit_cast.create(c.arena, to_cast), 3390 }); 3391 const subtrahend = try Tag.bit_not.create(c.arena, bitcast_node); 3392 const difference = try Tag.sub.create(c.arena, .{ 3393 .lhs = minuend, 3394 .rhs = subtrahend, 3395 }); 3396 const else_body = try Tag.break_val.create(c.arena, .{ 3397 .label = block_scope.label, 3398 .val = difference, 3399 }); 3400 3401 const if_node = try Tag.@"if".create(c.arena, .{ 3402 .cond = cond_node, 3403 .then = then_body, 3404 .@"else" = else_body, 3405 }); 3406 3407 try block_scope.statements.append(if_node); 3408 const block_node = try block_scope.complete(c); 3409 3410 const derefed = try Tag.deref.create(c.arena, block_node); 3411 3412 return maybeSuppressResult(c, result_used, derefed); 3413 } 3414 3415 fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node { 3416 const base_stmt = stmt.getBase(); 3417 const base_qt = getExprQualType(c, base_stmt); 3418 const is_vector = cIsVector(base_qt); 3419 3420 const subscr_expr = stmt.getIdx(); 3421 const subscr_qt = getExprQualType(c, subscr_expr); 3422 const is_longlong = cIsLongLongInteger(subscr_qt); 3423 const is_signed = cIsSignedInteger(subscr_qt); 3424 const is_nonnegative_int_literal = cIsNonNegativeIntLiteral(c, subscr_expr); 3425 3426 // Unwrap the base statement if it's an array decayed to a bare pointer type 3427 // so that we index the array itself 3428 var unwrapped_base = base_stmt; 3429 if (@as(*const clang.Stmt, @ptrCast(base_stmt)).getStmtClass() == .ImplicitCastExprClass) { 3430 const implicit_cast = @as(*const clang.ImplicitCastExpr, @ptrCast(base_stmt)); 3431 3432 if (implicit_cast.getCastKind() == .ArrayToPointerDecay) { 3433 unwrapped_base = implicit_cast.getSubExpr(); 3434 } 3435 } 3436 3437 // Special case: actual pointer (not decayed array) and signed integer subscript 3438 // See discussion at https://github.com/ziglang/zig/pull/8589 3439 if (is_signed and (base_stmt == unwrapped_base) and !is_vector and !is_nonnegative_int_literal) 3440 return transSignedArrayAccess(c, scope, base_stmt, subscr_expr, result_used); 3441 3442 const container_node = try transExpr(c, scope, unwrapped_base, .used); 3443 const rhs = if (is_longlong or is_signed) blk: { 3444 // check if long long first so that signed long long doesn't just become unsigned long long 3445 const typeid_node = if (is_longlong) try Tag.type.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, subscr_qt, false); 3446 break :blk try Tag.as.create(c.arena, .{ 3447 .lhs = typeid_node, 3448 .rhs = try Tag.int_cast.create( 3449 c.arena, 3450 try transExpr(c, scope, subscr_expr, .used), 3451 ), 3452 }); 3453 } else try transExpr(c, scope, subscr_expr, .used); 3454 3455 const node = try Tag.array_access.create(c.arena, .{ 3456 .lhs = container_node, 3457 .rhs = rhs, 3458 }); 3459 return maybeSuppressResult(c, result_used, node); 3460 } 3461 3462 /// Check if an expression is ultimately a reference to a function declaration 3463 /// (which means it should not be unwrapped with `.?` in translated code) 3464 fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { 3465 switch (expr.getStmtClass()) { 3466 .ParenExprClass => { 3467 const op_expr = @as(*const clang.ParenExpr, @ptrCast(expr)).getSubExpr(); 3468 return cIsFunctionDeclRef(op_expr); 3469 }, 3470 .DeclRefExprClass => { 3471 const decl_ref = @as(*const clang.DeclRefExpr, @ptrCast(expr)); 3472 const value_decl = decl_ref.getDecl(); 3473 const qt = value_decl.getType(); 3474 return qualTypeChildIsFnProto(qt); 3475 }, 3476 .ImplicitCastExprClass => { 3477 const implicit_cast = @as(*const clang.ImplicitCastExpr, @ptrCast(expr)); 3478 const cast_kind = implicit_cast.getCastKind(); 3479 if (cast_kind == .BuiltinFnToFnPtr) return true; 3480 if (cast_kind == .FunctionToPointerDecay) { 3481 return cIsFunctionDeclRef(implicit_cast.getSubExpr()); 3482 } 3483 return false; 3484 }, 3485 .UnaryOperatorClass => { 3486 const un_op = @as(*const clang.UnaryOperator, @ptrCast(expr)); 3487 const opcode = un_op.getOpcode(); 3488 return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr()); 3489 }, 3490 .GenericSelectionExprClass => { 3491 const gen_sel = @as(*const clang.GenericSelectionExpr, @ptrCast(expr)); 3492 return cIsFunctionDeclRef(gen_sel.getResultExpr()); 3493 }, 3494 else => return false, 3495 } 3496 } 3497 3498 fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node { 3499 const callee = stmt.getCallee(); 3500 const raw_fn_expr = try transExpr(c, scope, callee, .used); 3501 3502 var is_ptr = false; 3503 const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr); 3504 3505 const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee)) 3506 try Tag.unwrap.create(c.arena, raw_fn_expr) 3507 else 3508 raw_fn_expr; 3509 3510 const num_args = stmt.getNumArgs(); 3511 const args = try c.arena.alloc(Node, num_args); 3512 3513 const c_args = stmt.getArgs(); 3514 var i: usize = 0; 3515 while (i < num_args) : (i += 1) { 3516 var arg = try transExpr(c, scope, c_args[i], .used); 3517 3518 // In C the result type of a boolean expression is int. If this result is passed as 3519 // an argument to a function whose parameter is also int, there is no cast. Therefore 3520 // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int). 3521 if (fn_ty) |ty| { 3522 switch (ty) { 3523 .Proto => |fn_proto| { 3524 const param_count = fn_proto.getNumParams(); 3525 if (i < param_count) { 3526 const param_qt = fn_proto.getParamType(@as(c_uint, @intCast(i))); 3527 if (isBoolRes(arg) and cIsNativeInt(param_qt)) { 3528 arg = try Tag.int_from_bool.create(c.arena, arg); 3529 } else if (arg.tag() == .string_literal and qualTypeIsCharStar(param_qt)) { 3530 const loc = @as(*const clang.Stmt, @ptrCast(stmt)).getBeginLoc(); 3531 const dst_type_node = try transQualType(c, scope, param_qt, loc); 3532 arg = try removeCVQualifiers(c, dst_type_node, arg); 3533 } 3534 } 3535 }, 3536 else => {}, 3537 } 3538 } 3539 args[i] = arg; 3540 } 3541 const node = try Tag.call.create(c.arena, .{ .lhs = fn_expr, .args = args }); 3542 if (fn_ty) |ty| { 3543 const canon = ty.getReturnType().getCanonicalType(); 3544 const ret_ty = canon.getTypePtr(); 3545 if (ret_ty.isVoidType()) { 3546 return node; 3547 } 3548 } 3549 3550 return maybeSuppressResult(c, result_used, node); 3551 } 3552 3553 const ClangFunctionType = union(enum) { 3554 Proto: *const clang.FunctionProtoType, 3555 NoProto: *const clang.FunctionType, 3556 3557 fn getReturnType(self: @This()) clang.QualType { 3558 switch (@as(meta.Tag(@This()), self)) { 3559 .Proto => return self.Proto.getReturnType(), 3560 .NoProto => return self.NoProto.getReturnType(), 3561 } 3562 } 3563 }; 3564 3565 fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType { 3566 const canon = qt.getCanonicalType(); 3567 var ty = canon.getTypePtr(); 3568 is_ptr.* = false; 3569 3570 if (ty.getTypeClass() == .Pointer) { 3571 is_ptr.* = true; 3572 const child_qt = ty.getPointeeType(); 3573 ty = child_qt.getTypePtr(); 3574 } 3575 if (ty.getTypeClass() == .FunctionProto) { 3576 return ClangFunctionType{ .Proto = @as(*const clang.FunctionProtoType, @ptrCast(ty)) }; 3577 } 3578 if (ty.getTypeClass() == .FunctionNoProto) { 3579 return ClangFunctionType{ .NoProto = @as(*const clang.FunctionType, @ptrCast(ty)) }; 3580 } 3581 return null; 3582 } 3583 3584 fn transUnaryExprOrTypeTraitExpr( 3585 c: *Context, 3586 scope: *Scope, 3587 stmt: *const clang.UnaryExprOrTypeTraitExpr, 3588 result_used: ResultUsed, 3589 ) TransError!Node { 3590 const loc = stmt.getBeginLoc(); 3591 const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc); 3592 3593 const kind = stmt.getKind(); 3594 const node = switch (kind) { 3595 .SizeOf => try Tag.sizeof.create(c.arena, type_node), 3596 .AlignOf => try Tag.alignof.create(c.arena, type_node), 3597 .PreferredAlignOf, 3598 .VecStep, 3599 .OpenMPRequiredSimdAlign, 3600 => return fail( 3601 c, 3602 error.UnsupportedTranslation, 3603 loc, 3604 "unsupported type trait kind {}", 3605 .{kind}, 3606 ), 3607 }; 3608 return maybeSuppressResult(c, result_used, node); 3609 } 3610 3611 fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { 3612 if (cIsUnsignedInteger(qt)) { 3613 // unsigned integer overflow wraps around. 3614 return true; 3615 } else { 3616 // float, signed integer, and pointer overflow is undefined behavior. 3617 return false; 3618 } 3619 } 3620 3621 fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!Node { 3622 const op_expr = stmt.getSubExpr(); 3623 switch (stmt.getOpcode()) { 3624 .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType())) 3625 return transCreatePostCrement(c, scope, stmt, .add_wrap_assign, used) 3626 else 3627 return transCreatePostCrement(c, scope, stmt, .add_assign, used), 3628 .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType())) 3629 return transCreatePostCrement(c, scope, stmt, .sub_wrap_assign, used) 3630 else 3631 return transCreatePostCrement(c, scope, stmt, .sub_assign, used), 3632 .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType())) 3633 return transCreatePreCrement(c, scope, stmt, .add_wrap_assign, used) 3634 else 3635 return transCreatePreCrement(c, scope, stmt, .add_assign, used), 3636 .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType())) 3637 return transCreatePreCrement(c, scope, stmt, .sub_wrap_assign, used) 3638 else 3639 return transCreatePreCrement(c, scope, stmt, .sub_assign, used), 3640 .AddrOf => { 3641 return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used)); 3642 }, 3643 .Deref => { 3644 if (qualTypeWasDemotedToOpaque(c, stmt.getType())) 3645 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "cannot dereference opaque type", .{}); 3646 3647 const node = try transExpr(c, scope, op_expr, used); 3648 var is_ptr = false; 3649 const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr); 3650 if (fn_ty != null and is_ptr) 3651 return node; 3652 return Tag.deref.create(c.arena, node); 3653 }, 3654 .Plus => return transExpr(c, scope, op_expr, used), 3655 .Minus => { 3656 if (!qualTypeHasWrappingOverflow(op_expr.getType())) { 3657 const sub_expr_node = try transExpr(c, scope, op_expr, .used); 3658 const to_negate = if (isBoolRes(sub_expr_node)) blk: { 3659 const ty_node = try Tag.type.create(c.arena, "c_int"); 3660 const int_node = try Tag.int_from_bool.create(c.arena, sub_expr_node); 3661 break :blk try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = int_node }); 3662 } else sub_expr_node; 3663 return Tag.negate.create(c.arena, to_negate); 3664 } else if (cIsUnsignedInteger(op_expr.getType())) { 3665 // use -% x for unsigned integers 3666 return Tag.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used)); 3667 } else return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); 3668 }, 3669 .Not => { 3670 return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used)); 3671 }, 3672 .LNot => { 3673 return Tag.not.create(c.arena, try transBoolExpr(c, scope, op_expr, .used)); 3674 }, 3675 .Extension => { 3676 return transExpr(c, scope, stmt.getSubExpr(), used); 3677 }, 3678 else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), 3679 } 3680 } 3681 3682 fn transCreatePreCrement( 3683 c: *Context, 3684 scope: *Scope, 3685 stmt: *const clang.UnaryOperator, 3686 op: Tag, 3687 used: ResultUsed, 3688 ) TransError!Node { 3689 const op_expr = stmt.getSubExpr(); 3690 3691 if (used == .unused) { 3692 // common case 3693 // c: ++expr 3694 // zig: expr += 1 3695 const lhs = try transExpr(c, scope, op_expr, .used); 3696 const rhs = Tag.one_literal.init(); 3697 return transCreateNodeInfixOp(c, op, lhs, rhs, .used); 3698 } 3699 // worst case 3700 // c: ++expr 3701 // zig: (blk: { 3702 // zig: const _ref = &expr; 3703 // zig: _ref.* += 1; 3704 // zig: break :blk _ref.* 3705 // zig: }) 3706 var block_scope = try Scope.Block.init(c, scope, true); 3707 defer block_scope.deinit(); 3708 3709 const ref = try block_scope.reserveMangledName(c, "ref"); 3710 const expr = try transExpr(c, &block_scope.base, op_expr, .used); 3711 const addr_of = try Tag.address_of.create(c.arena, expr); 3712 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); 3713 try block_scope.statements.append(ref_decl); 3714 3715 const lhs_node = try Tag.identifier.create(c.arena, ref); 3716 const ref_node = try Tag.deref.create(c.arena, lhs_node); 3717 const node = try transCreateNodeInfixOp(c, op, ref_node, Tag.one_literal.init(), .used); 3718 try block_scope.statements.append(node); 3719 3720 const break_node = try Tag.break_val.create(c.arena, .{ 3721 .label = block_scope.label, 3722 .val = ref_node, 3723 }); 3724 try block_scope.statements.append(break_node); 3725 return block_scope.complete(c); 3726 } 3727 3728 fn transCreatePostCrement( 3729 c: *Context, 3730 scope: *Scope, 3731 stmt: *const clang.UnaryOperator, 3732 op: Tag, 3733 used: ResultUsed, 3734 ) TransError!Node { 3735 const op_expr = stmt.getSubExpr(); 3736 3737 if (used == .unused) { 3738 // common case 3739 // c: expr++ 3740 // zig: expr += 1 3741 const lhs = try transExpr(c, scope, op_expr, .used); 3742 const rhs = Tag.one_literal.init(); 3743 return transCreateNodeInfixOp(c, op, lhs, rhs, .used); 3744 } 3745 // worst case 3746 // c: expr++ 3747 // zig: (blk: { 3748 // zig: const _ref = &expr; 3749 // zig: const _tmp = _ref.*; 3750 // zig: _ref.* += 1; 3751 // zig: break :blk _tmp 3752 // zig: }) 3753 var block_scope = try Scope.Block.init(c, scope, true); 3754 defer block_scope.deinit(); 3755 const ref = try block_scope.reserveMangledName(c, "ref"); 3756 const tmp = try block_scope.reserveMangledName(c, "tmp"); 3757 3758 const expr = try transExpr(c, &block_scope.base, op_expr, .used); 3759 const addr_of = try Tag.address_of.create(c.arena, expr); 3760 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); 3761 try block_scope.statements.append(ref_decl); 3762 3763 const lhs_node = try Tag.identifier.create(c.arena, ref); 3764 const ref_node = try Tag.deref.create(c.arena, lhs_node); 3765 3766 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node }); 3767 try block_scope.statements.append(tmp_decl); 3768 3769 const node = try transCreateNodeInfixOp(c, op, ref_node, Tag.one_literal.init(), .used); 3770 try block_scope.statements.append(node); 3771 3772 const break_node = try Tag.break_val.create(c.arena, .{ 3773 .label = block_scope.label, 3774 .val = try Tag.identifier.create(c.arena, tmp), 3775 }); 3776 try block_scope.statements.append(break_node); 3777 return block_scope.complete(c); 3778 } 3779 3780 fn transCompoundAssignOperator(c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!Node { 3781 switch (stmt.getOpcode()) { 3782 .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) 3783 return transCreateCompoundAssign(c, scope, stmt, .mul_wrap_assign, used) 3784 else 3785 return transCreateCompoundAssign(c, scope, stmt, .mul_assign, used), 3786 .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) 3787 return transCreateCompoundAssign(c, scope, stmt, .add_wrap_assign, used) 3788 else 3789 return transCreateCompoundAssign(c, scope, stmt, .add_assign, used), 3790 .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) 3791 return transCreateCompoundAssign(c, scope, stmt, .sub_wrap_assign, used) 3792 else 3793 return transCreateCompoundAssign(c, scope, stmt, .sub_assign, used), 3794 .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .div_assign, used), 3795 .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .mod_assign, used), 3796 .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .shl_assign, used), 3797 .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .shr_assign, used), 3798 .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_and_assign, used), 3799 .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_xor_assign, used), 3800 .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_or_assign, used), 3801 else => return fail( 3802 c, 3803 error.UnsupportedTranslation, 3804 stmt.getBeginLoc(), 3805 "unsupported C translation {}", 3806 .{stmt.getOpcode()}, 3807 ), 3808 } 3809 } 3810 3811 fn transCreateCompoundAssign( 3812 c: *Context, 3813 scope: *Scope, 3814 stmt: *const clang.CompoundAssignOperator, 3815 op: Tag, 3816 used: ResultUsed, 3817 ) TransError!Node { 3818 const is_shift = op == .shl_assign or op == .shr_assign; 3819 const is_div = op == .div_assign; 3820 const is_mod = op == .mod_assign; 3821 const lhs = stmt.getLHS(); 3822 const rhs = stmt.getRHS(); 3823 const loc = stmt.getBeginLoc(); 3824 const lhs_qt = getExprQualType(c, lhs); 3825 const rhs_qt = getExprQualType(c, rhs); 3826 const is_signed = cIsSignedInteger(lhs_qt); 3827 const is_ptr_op_signed = qualTypeIsPtr(lhs_qt) and cIsSignedInteger(rhs_qt); 3828 const requires_cast = !lhs_qt.eq(rhs_qt) and !is_ptr_op_signed; 3829 3830 if (used == .unused) { 3831 // common case 3832 // c: lhs += rhs 3833 // zig: lhs += rhs 3834 const lhs_node = try transExpr(c, scope, lhs, .used); 3835 var rhs_node = try transExpr(c, scope, rhs, .used); 3836 if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); 3837 3838 if ((is_mod or is_div) and is_signed) { 3839 if (requires_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); 3840 const operands = .{ .lhs = lhs_node, .rhs = rhs_node }; 3841 const builtin = if (is_mod) 3842 try Tag.signed_remainder.create(c.arena, operands) 3843 else 3844 try Tag.div_trunc.create(c.arena, operands); 3845 3846 return transCreateNodeInfixOp(c, .assign, lhs_node, builtin, .used); 3847 } 3848 3849 if (is_shift) { 3850 rhs_node = try Tag.int_cast.create(c.arena, rhs_node); 3851 } else if (requires_cast) { 3852 rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); 3853 } 3854 return transCreateNodeInfixOp(c, op, lhs_node, rhs_node, .used); 3855 } 3856 // worst case 3857 // c: lhs += rhs 3858 // zig: (blk: { 3859 // zig: const _ref = &lhs; 3860 // zig: _ref.* += rhs; 3861 // zig: break :blk _ref.* 3862 // zig: }) 3863 var block_scope = try Scope.Block.init(c, scope, true); 3864 defer block_scope.deinit(); 3865 const ref = try block_scope.reserveMangledName(c, "ref"); 3866 3867 const expr = try transExpr(c, &block_scope.base, lhs, .used); 3868 const addr_of = try Tag.address_of.create(c.arena, expr); 3869 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); 3870 try block_scope.statements.append(ref_decl); 3871 3872 const lhs_node = try Tag.identifier.create(c.arena, ref); 3873 const ref_node = try Tag.deref.create(c.arena, lhs_node); 3874 3875 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); 3876 if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); 3877 if ((is_mod or is_div) and is_signed) { 3878 if (requires_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node); 3879 const operands = .{ .lhs = ref_node, .rhs = rhs_node }; 3880 const builtin = if (is_mod) 3881 try Tag.signed_remainder.create(c.arena, operands) 3882 else 3883 try Tag.div_trunc.create(c.arena, operands); 3884 3885 const assign = try transCreateNodeInfixOp(c, .assign, ref_node, builtin, .used); 3886 try block_scope.statements.append(assign); 3887 } else { 3888 if (is_shift) { 3889 rhs_node = try Tag.int_cast.create(c.arena, rhs_node); 3890 } else if (requires_cast) { 3891 rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node); 3892 } 3893 3894 const assign = try transCreateNodeInfixOp(c, op, ref_node, rhs_node, .used); 3895 try block_scope.statements.append(assign); 3896 } 3897 3898 const break_node = try Tag.break_val.create(c.arena, .{ 3899 .label = block_scope.label, 3900 .val = ref_node, 3901 }); 3902 try block_scope.statements.append(break_node); 3903 return block_scope.complete(c); 3904 } 3905 3906 fn removeCVQualifiers(c: *Context, dst_type_node: Node, expr: Node) Error!Node { 3907 const const_casted = try Tag.const_cast.create(c.arena, expr); 3908 const volatile_casted = try Tag.volatile_cast.create(c.arena, const_casted); 3909 return Tag.as.create(c.arena, .{ 3910 .lhs = dst_type_node, 3911 .rhs = try Tag.ptr_cast.create(c.arena, volatile_casted), 3912 }); 3913 } 3914 3915 fn transCPtrCast( 3916 c: *Context, 3917 scope: *Scope, 3918 loc: clang.SourceLocation, 3919 dst_type: clang.QualType, 3920 src_type: clang.QualType, 3921 expr: Node, 3922 ) !Node { 3923 const ty = dst_type.getTypePtr(); 3924 const child_type = ty.getPointeeType(); 3925 const src_ty = src_type.getTypePtr(); 3926 const src_child_type = src_ty.getPointeeType(); 3927 const dst_type_node = try transType(c, scope, ty, loc); 3928 3929 if (!src_ty.isArrayType() and ((src_child_type.isConstQualified() and 3930 !child_type.isConstQualified()) or 3931 (src_child_type.isVolatileQualified() and 3932 !child_type.isVolatileQualified()))) 3933 { 3934 return removeCVQualifiers(c, dst_type_node, expr); 3935 } else { 3936 // Implicit downcasting from higher to lower alignment values is forbidden, 3937 // use @alignCast to side-step this problem 3938 const rhs = if (qualTypeCanon(child_type).isVoidType()) 3939 // void has 1-byte alignment, so @alignCast is not needed 3940 expr 3941 else if (typeIsOpaque(c, qualTypeCanon(child_type), loc)) 3942 // For opaque types a ptrCast is enough 3943 expr 3944 else blk: { 3945 break :blk try Tag.align_cast.create(c.arena, expr); 3946 }; 3947 return Tag.as.create(c.arena, .{ 3948 .lhs = dst_type_node, 3949 .rhs = try Tag.ptr_cast.create(c.arena, rhs), 3950 }); 3951 } 3952 } 3953 3954 fn transFloatingLiteral(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { 3955 // TODO use something more accurate than widening to a larger float type and printing that result 3956 switch (expr.getRawSemantics()) { 3957 .IEEEhalf, // f16 3958 .IEEEsingle, // f32 3959 .IEEEdouble, // f64 3960 => { 3961 var dbl = expr.getValueAsApproximateDouble(); 3962 const is_negative = dbl < 0; // -0.0 is considered non-negative 3963 if (is_negative) dbl = -dbl; 3964 const str = if (dbl == @floor(dbl)) 3965 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl}) 3966 else 3967 try std.fmt.allocPrint(c.arena, "{d}", .{dbl}); 3968 var node = try Tag.float_literal.create(c.arena, str); 3969 if (is_negative) node = try Tag.negate.create(c.arena, node); 3970 return maybeSuppressResult(c, used, node); 3971 }, 3972 .x87DoubleExtended, // f80 3973 .IEEEquad, // f128 3974 => return transFloatingLiteralQuad(c, expr, used), 3975 else => |format| return fail( 3976 c, 3977 error.UnsupportedTranslation, 3978 expr.getBeginLoc(), 3979 "unsupported floating point constant format {}", 3980 .{format}, 3981 ), 3982 } 3983 } 3984 3985 fn transFloatingLiteralQuad(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { 3986 assert(switch (expr.getRawSemantics()) { 3987 .x87DoubleExtended, .IEEEquad => true, 3988 else => false, 3989 }); 3990 3991 var low: u64 = undefined; 3992 var high: u64 = undefined; 3993 expr.getValueAsApproximateQuadBits(&low, &high); 3994 var quad: f128 = @bitCast(low | @as(u128, high) << 64); 3995 const is_negative = quad < 0; // -0.0 is considered non-negative 3996 if (is_negative) quad = -quad; 3997 3998 // TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181> 3999 // in the meantime, if the value can be roundtripped by casting it to f64, serializing it to 4000 // the decimal format and parsing it back as the exact same f128 value, then use that serialized form 4001 const str = fmt_decimal: { 4002 var buf: [512]u8 = undefined; // should be large enough to print any f64 in decimal form 4003 const dbl: f64 = @floatCast(quad); 4004 const temp_str = if (dbl == @floor(dbl)) 4005 std.fmt.bufPrint(&buf, "{d}.0", .{dbl}) catch |err| switch (err) { 4006 error.NoSpaceLeft => unreachable, 4007 } 4008 else 4009 std.fmt.bufPrint(&buf, "{d}", .{dbl}) catch |err| switch (err) { 4010 error.NoSpaceLeft => unreachable, 4011 }; 4012 const could_roundtrip = if (std.fmt.parseFloat(f128, temp_str)) |parsed_quad| 4013 quad == parsed_quad 4014 else |_| 4015 false; 4016 break :fmt_decimal if (could_roundtrip) try c.arena.dupe(u8, temp_str) else null; 4017 } 4018 // otherwise, fall back to the hexadecimal format 4019 orelse try std.fmt.allocPrint(c.arena, "{x}", .{quad}); 4020 4021 var node = try Tag.float_literal.create(c.arena, str); 4022 if (is_negative) node = try Tag.negate.create(c.arena, node); 4023 return maybeSuppressResult(c, used, node); 4024 } 4025 4026 fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!Node { 4027 // GNU extension of the ternary operator where the middle expression is 4028 // omitted, the condition itself is returned if it evaluates to true 4029 const qt = @as(*const clang.Expr, @ptrCast(stmt)).getType(); 4030 const res_is_bool = qualTypeIsBoolean(qt); 4031 const casted_stmt = @as(*const clang.AbstractConditionalOperator, @ptrCast(stmt)); 4032 const cond_expr = casted_stmt.getCond(); 4033 const false_expr = casted_stmt.getFalseExpr(); 4034 4035 // c: (cond_expr)?:(false_expr) 4036 // zig: (blk: { 4037 // const _cond_temp = (cond_expr); 4038 // break :blk if (_cond_temp) _cond_temp else (false_expr); 4039 // }) 4040 var block_scope = try Scope.Block.init(c, scope, true); 4041 defer block_scope.deinit(); 4042 4043 const cond_temp = try block_scope.reserveMangledName(c, "cond_temp"); 4044 const init_node = try transExpr(c, &block_scope.base, cond_expr, .used); 4045 const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = cond_temp, .init = init_node }); 4046 try block_scope.statements.append(ref_decl); 4047 4048 var cond_scope = Scope.Condition{ 4049 .base = .{ 4050 .parent = &block_scope.base, 4051 .id = .condition, 4052 }, 4053 }; 4054 defer cond_scope.deinit(); 4055 4056 const cond_ident = try Tag.identifier.create(c.arena, cond_temp); 4057 const ty = getExprQualType(c, cond_expr).getTypePtr(); 4058 const cond_node = try finishBoolExpr(c, &cond_scope.base, cond_expr.getBeginLoc(), ty, cond_ident, .used); 4059 var then_body = cond_ident; 4060 if (!res_is_bool and isBoolRes(init_node)) { 4061 then_body = try Tag.int_from_bool.create(c.arena, then_body); 4062 } 4063 4064 var else_body = try transExpr(c, &block_scope.base, false_expr, .used); 4065 if (!res_is_bool and isBoolRes(else_body)) { 4066 else_body = try Tag.int_from_bool.create(c.arena, else_body); 4067 } 4068 const if_node = try Tag.@"if".create(c.arena, .{ 4069 .cond = cond_node, 4070 .then = then_body, 4071 .@"else" = else_body, 4072 }); 4073 const break_node = try Tag.break_val.create(c.arena, .{ 4074 .label = block_scope.label, 4075 .val = if_node, 4076 }); 4077 try block_scope.statements.append(break_node); 4078 const res = try block_scope.complete(c); 4079 return maybeSuppressResult(c, used, res); 4080 } 4081 4082 fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!Node { 4083 var cond_scope = Scope.Condition{ 4084 .base = .{ 4085 .parent = scope, 4086 .id = .condition, 4087 }, 4088 }; 4089 defer cond_scope.deinit(); 4090 4091 const qt = @as(*const clang.Expr, @ptrCast(stmt)).getType(); 4092 const res_is_bool = qualTypeIsBoolean(qt); 4093 const casted_stmt = @as(*const clang.AbstractConditionalOperator, @ptrCast(stmt)); 4094 const cond_expr = casted_stmt.getCond(); 4095 const true_expr = casted_stmt.getTrueExpr(); 4096 const false_expr = casted_stmt.getFalseExpr(); 4097 4098 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); 4099 4100 var then_body = try transExpr(c, scope, true_expr, used); 4101 if (!res_is_bool and isBoolRes(then_body)) { 4102 then_body = try Tag.int_from_bool.create(c.arena, then_body); 4103 } 4104 4105 var else_body = try transExpr(c, scope, false_expr, used); 4106 if (!res_is_bool and isBoolRes(else_body)) { 4107 else_body = try Tag.int_from_bool.create(c.arena, else_body); 4108 } 4109 4110 const if_node = try Tag.@"if".create(c.arena, .{ 4111 .cond = cond, 4112 .then = then_body, 4113 .@"else" = else_body, 4114 }); 4115 // Clang inserts ImplicitCast(ToVoid)'s to both rhs and lhs so we don't need to suppress the result here. 4116 return if_node; 4117 } 4118 4119 fn maybeSuppressResult(c: *Context, used: ResultUsed, result: Node) TransError!Node { 4120 if (used == .used) return result; 4121 return Tag.discard.create(c.arena, .{ .should_skip = false, .value = result }); 4122 } 4123 4124 fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { 4125 const gop = try c.global_scope.sym_table.getOrPut(name); 4126 if (!gop.found_existing) { 4127 gop.value_ptr.* = decl_node; 4128 try c.global_scope.nodes.append(decl_node); 4129 } 4130 } 4131 4132 fn transQualTypeInitializedStringLiteral(c: *Context, elem_ty: Node, string_lit: *const clang.StringLiteral) TypeError!Node { 4133 const string_lit_size = string_lit.getLength(); 4134 const array_size = @as(usize, @intCast(string_lit_size)); 4135 4136 // incomplete array initialized with empty string, will be translated as [1]T{0} 4137 // see https://github.com/ziglang/zig/issues/8256 4138 if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty }); 4139 4140 return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); 4141 } 4142 4143 /// Translate a qualtype for a variable with an initializer. This only matters 4144 /// for incomplete arrays, since the initializer determines the size of the array. 4145 fn transQualTypeInitialized( 4146 c: *Context, 4147 scope: *Scope, 4148 qt: clang.QualType, 4149 decl_init: *const clang.Expr, 4150 source_loc: clang.SourceLocation, 4151 ) TypeError!Node { 4152 const ty = qt.getTypePtr(); 4153 if (ty.getTypeClass() == .IncompleteArray) { 4154 const incomplete_array_ty = @as(*const clang.IncompleteArrayType, @ptrCast(ty)); 4155 const elem_ty = try transType(c, scope, incomplete_array_ty.getElementType().getTypePtr(), source_loc); 4156 4157 switch (decl_init.getStmtClass()) { 4158 .StringLiteralClass => { 4159 const string_lit = @as(*const clang.StringLiteral, @ptrCast(decl_init)); 4160 return transQualTypeInitializedStringLiteral(c, elem_ty, string_lit); 4161 }, 4162 .InitListExprClass => { 4163 const init_expr = @as(*const clang.InitListExpr, @ptrCast(decl_init)); 4164 const size = init_expr.getNumInits(); 4165 4166 if (init_expr.isStringLiteralInit()) { 4167 assert(size == 1); 4168 const string_lit = init_expr.getInit(0).castToStringLiteral().?; 4169 return transQualTypeInitializedStringLiteral(c, elem_ty, string_lit); 4170 } 4171 4172 return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty }); 4173 }, 4174 else => {}, 4175 } 4176 } 4177 return transQualType(c, scope, qt, source_loc); 4178 } 4179 4180 fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node { 4181 return transType(c, scope, qt.getTypePtr(), source_loc); 4182 } 4183 4184 /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. 4185 /// Asserts the type is an integer. 4186 fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!Node { 4187 return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed); 4188 } 4189 4190 /// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness. 4191 /// Asserts the type is an integer. 4192 fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!Node { 4193 assert(ty.getTypeClass() == .Builtin); 4194 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 4195 return Tag.type.create(c.arena, switch (builtin_ty.getKind()) { 4196 .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8", 4197 .UShort, .Short => if (is_signed) "c_short" else "c_ushort", 4198 .UInt, .Int => if (is_signed) "c_int" else "c_uint", 4199 .ULong, .Long => if (is_signed) "c_long" else "c_ulong", 4200 .ULongLong, .LongLong => if (is_signed) "c_longlong" else "c_ulonglong", 4201 .UInt128, .Int128 => if (is_signed) "i128" else "u128", 4202 .Char16 => if (is_signed) "i16" else "u16", 4203 .Char32 => if (is_signed) "i32" else "u32", 4204 else => unreachable, // only call this function when it has already been determined the type is int 4205 }); 4206 } 4207 4208 fn isCBuiltinType(qt: clang.QualType, kind: clang.BuiltinTypeKind) bool { 4209 const c_type = qualTypeCanon(qt); 4210 if (c_type.getTypeClass() != .Builtin) 4211 return false; 4212 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4213 return builtin_ty.getKind() == kind; 4214 } 4215 4216 fn qualTypeIsPtr(qt: clang.QualType) bool { 4217 return qualTypeCanon(qt).getTypeClass() == .Pointer; 4218 } 4219 4220 fn qualTypeIsBoolean(qt: clang.QualType) bool { 4221 return qualTypeCanon(qt).isBooleanType(); 4222 } 4223 4224 fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 { 4225 const ty = qt.getTypePtr(); 4226 4227 switch (ty.getTypeClass()) { 4228 .Builtin => { 4229 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 4230 4231 switch (builtin_ty.getKind()) { 4232 .Char_U, 4233 .UChar, 4234 .Char_S, 4235 .SChar, 4236 => return 8, 4237 .UInt128, 4238 .Int128, 4239 => return 128, 4240 else => return 0, 4241 } 4242 4243 unreachable; 4244 }, 4245 .Typedef => { 4246 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 4247 const typedef_decl = typedef_ty.getDecl(); 4248 const type_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(typedef_decl)).getName_bytes_begin()); 4249 4250 if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) { 4251 return 8; 4252 } else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) { 4253 return 16; 4254 } else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) { 4255 return 32; 4256 } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) { 4257 return 64; 4258 } else { 4259 return 0; 4260 } 4261 }, 4262 else => return 0, 4263 } 4264 } 4265 4266 fn qualTypeChildIsFnProto(qt: clang.QualType) bool { 4267 const ty = qualTypeCanon(qt); 4268 4269 switch (ty.getTypeClass()) { 4270 .FunctionProto, .FunctionNoProto => return true, 4271 else => return false, 4272 } 4273 } 4274 4275 fn qualTypeCanon(qt: clang.QualType) *const clang.Type { 4276 const canon = qt.getCanonicalType(); 4277 return canon.getTypePtr(); 4278 } 4279 4280 fn getExprQualType(c: *Context, expr: *const clang.Expr) clang.QualType { 4281 blk: { 4282 // If this is a C `char *`, turn it into a `const char *` 4283 if (expr.getStmtClass() != .ImplicitCastExprClass) break :blk; 4284 const cast_expr = @as(*const clang.ImplicitCastExpr, @ptrCast(expr)); 4285 if (cast_expr.getCastKind() != .ArrayToPointerDecay) break :blk; 4286 const sub_expr = cast_expr.getSubExpr(); 4287 if (sub_expr.getStmtClass() != .StringLiteralClass) break :blk; 4288 const array_qt = sub_expr.getType(); 4289 const array_type = @as(*const clang.ArrayType, @ptrCast(array_qt.getTypePtr())); 4290 var pointee_qt = array_type.getElementType(); 4291 pointee_qt.addConst(); 4292 return c.clang_context.getPointerType(pointee_qt); 4293 } 4294 return expr.getType(); 4295 } 4296 4297 fn typeIsOpaque(c: *Context, ty: *const clang.Type, loc: clang.SourceLocation) bool { 4298 switch (ty.getTypeClass()) { 4299 .Builtin => { 4300 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 4301 return builtin_ty.getKind() == .Void; 4302 }, 4303 .Record => { 4304 const record_ty = @as(*const clang.RecordType, @ptrCast(ty)); 4305 const record_decl = record_ty.getDecl(); 4306 const record_def = record_decl.getDefinition() orelse 4307 return true; 4308 var it = record_def.field_begin(); 4309 const end_it = record_def.field_end(); 4310 while (it.neq(end_it)) : (it = it.next()) { 4311 const field_decl = it.deref(); 4312 4313 if (field_decl.isBitField()) { 4314 return true; 4315 } 4316 } 4317 return false; 4318 }, 4319 .Elaborated => { 4320 const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty)); 4321 const qt = elaborated_ty.getNamedType(); 4322 return typeIsOpaque(c, qt.getTypePtr(), loc); 4323 }, 4324 .Typedef => { 4325 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 4326 const typedef_decl = typedef_ty.getDecl(); 4327 const underlying_type = typedef_decl.getUnderlyingType(); 4328 return typeIsOpaque(c, underlying_type.getTypePtr(), loc); 4329 }, 4330 else => return false, 4331 } 4332 } 4333 4334 /// plain `char *` (not const; not explicitly signed or unsigned) 4335 fn qualTypeIsCharStar(qt: clang.QualType) bool { 4336 if (qualTypeIsPtr(qt)) { 4337 const child_qt = qualTypeCanon(qt).getPointeeType(); 4338 return cIsUnqualifiedChar(child_qt) and !child_qt.isConstQualified(); 4339 } 4340 return false; 4341 } 4342 4343 /// C `char` without explicit signed or unsigned qualifier 4344 fn cIsUnqualifiedChar(qt: clang.QualType) bool { 4345 const c_type = qualTypeCanon(qt); 4346 if (c_type.getTypeClass() != .Builtin) return false; 4347 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4348 return switch (builtin_ty.getKind()) { 4349 .Char_S, .Char_U => true, 4350 else => false, 4351 }; 4352 } 4353 4354 fn cIsInteger(qt: clang.QualType) bool { 4355 return cIsSignedInteger(qt) or cIsUnsignedInteger(qt); 4356 } 4357 4358 fn cIsUnsignedInteger(qt: clang.QualType) bool { 4359 const c_type = qualTypeCanon(qt); 4360 if (c_type.getTypeClass() != .Builtin) return false; 4361 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4362 return switch (builtin_ty.getKind()) { 4363 .Char_U, 4364 .UChar, 4365 .Char_S, 4366 .UShort, 4367 .UInt, 4368 .ULong, 4369 .ULongLong, 4370 .UInt128, 4371 .WChar_U, 4372 => true, 4373 else => false, 4374 }; 4375 } 4376 4377 fn cIntTypeToIndex(qt: clang.QualType) u8 { 4378 const c_type = qualTypeCanon(qt); 4379 assert(c_type.getTypeClass() == .Builtin); 4380 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4381 return switch (builtin_ty.getKind()) { 4382 .Bool, .Char_U, .Char_S, .UChar, .SChar, .Char8 => 1, 4383 .WChar_U, .WChar_S => 2, 4384 .UShort, .Short, .Char16 => 3, 4385 .UInt, .Int, .Char32 => 4, 4386 .ULong, .Long => 5, 4387 .ULongLong, .LongLong => 6, 4388 .UInt128, .Int128 => 7, 4389 else => unreachable, 4390 }; 4391 } 4392 4393 fn cIntTypeCmp(a: clang.QualType, b: clang.QualType) math.Order { 4394 const a_index = cIntTypeToIndex(a); 4395 const b_index = cIntTypeToIndex(b); 4396 return math.order(a_index, b_index); 4397 } 4398 4399 /// Checks if expr is an integer literal >= 0 4400 fn cIsNonNegativeIntLiteral(c: *Context, expr: *const clang.Expr) bool { 4401 if (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass() == .IntegerLiteralClass) { 4402 var signum: c_int = undefined; 4403 if (!(@as(*const clang.IntegerLiteral, @ptrCast(expr)).getSignum(&signum, c.clang_context))) { 4404 return false; 4405 } 4406 return signum >= 0; 4407 } 4408 return false; 4409 } 4410 4411 fn cIsSignedInteger(qt: clang.QualType) bool { 4412 const c_type = qualTypeCanon(qt); 4413 if (c_type.getTypeClass() != .Builtin) return false; 4414 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4415 return switch (builtin_ty.getKind()) { 4416 .SChar, 4417 .Short, 4418 .Int, 4419 .Long, 4420 .LongLong, 4421 .Int128, 4422 .WChar_S, 4423 => true, 4424 else => false, 4425 }; 4426 } 4427 4428 fn cIsNativeInt(qt: clang.QualType) bool { 4429 const c_type = qualTypeCanon(qt); 4430 if (c_type.getTypeClass() != .Builtin) return false; 4431 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4432 return builtin_ty.getKind() == .Int; 4433 } 4434 4435 fn cIsFloating(qt: clang.QualType) bool { 4436 const c_type = qualTypeCanon(qt); 4437 if (c_type.getTypeClass() != .Builtin) return false; 4438 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4439 return switch (builtin_ty.getKind()) { 4440 .Float, 4441 .Double, 4442 .Float128, 4443 .LongDouble, 4444 => true, 4445 else => false, 4446 }; 4447 } 4448 4449 fn cIsLongLongInteger(qt: clang.QualType) bool { 4450 const c_type = qualTypeCanon(qt); 4451 if (c_type.getTypeClass() != .Builtin) return false; 4452 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type)); 4453 return switch (builtin_ty.getKind()) { 4454 .LongLong, .ULongLong, .Int128, .UInt128 => true, 4455 else => false, 4456 }; 4457 } 4458 fn transCreateNodeAssign( 4459 c: *Context, 4460 scope: *Scope, 4461 result_used: ResultUsed, 4462 lhs: *const clang.Expr, 4463 rhs: *const clang.Expr, 4464 ) !Node { 4465 // common case 4466 // c: lhs = rhs 4467 // zig: lhs = rhs 4468 if (result_used == .unused) { 4469 const lhs_node = try transExpr(c, scope, lhs, .used); 4470 var rhs_node = try transExprCoercing(c, scope, rhs, .used); 4471 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { 4472 rhs_node = try Tag.int_from_bool.create(c.arena, rhs_node); 4473 } 4474 return transCreateNodeInfixOp(c, .assign, lhs_node, rhs_node, .used); 4475 } 4476 4477 // worst case 4478 // c: lhs = rhs 4479 // zig: (blk: { 4480 // zig: const _tmp = rhs; 4481 // zig: lhs = _tmp; 4482 // zig: break :blk _tmp 4483 // zig: }) 4484 var block_scope = try Scope.Block.init(c, scope, true); 4485 defer block_scope.deinit(); 4486 4487 const tmp = try block_scope.reserveMangledName(c, "tmp"); 4488 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); 4489 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { 4490 rhs_node = try Tag.int_from_bool.create(c.arena, rhs_node); 4491 } 4492 4493 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); 4494 try block_scope.statements.append(tmp_decl); 4495 4496 const lhs_node = try transExpr(c, &block_scope.base, lhs, .used); 4497 const tmp_ident = try Tag.identifier.create(c.arena, tmp); 4498 const assign = try transCreateNodeInfixOp(c, .assign, lhs_node, tmp_ident, .used); 4499 try block_scope.statements.append(assign); 4500 4501 const break_node = try Tag.break_val.create(c.arena, .{ 4502 .label = block_scope.label, 4503 .val = tmp_ident, 4504 }); 4505 try block_scope.statements.append(break_node); 4506 return block_scope.complete(c); 4507 } 4508 4509 fn transCreateNodeInfixOp( 4510 c: *Context, 4511 op: Tag, 4512 lhs: Node, 4513 rhs: Node, 4514 used: ResultUsed, 4515 ) !Node { 4516 const payload = try c.arena.create(ast.Payload.BinOp); 4517 payload.* = .{ 4518 .base = .{ .tag = op }, 4519 .data = .{ 4520 .lhs = lhs, 4521 .rhs = rhs, 4522 }, 4523 }; 4524 return maybeSuppressResult(c, used, Node.initPayload(&payload.base)); 4525 } 4526 4527 fn transCreateNodeBoolInfixOp( 4528 c: *Context, 4529 scope: *Scope, 4530 stmt: *const clang.BinaryOperator, 4531 op: Tag, 4532 used: ResultUsed, 4533 ) !Node { 4534 std.debug.assert(op == .@"and" or op == .@"or"); 4535 4536 const lhs = try transBoolExpr(c, scope, stmt.getLHS(), .used); 4537 const rhs = try transBoolExpr(c, scope, stmt.getRHS(), .used); 4538 4539 return transCreateNodeInfixOp(c, op, lhs, rhs, used); 4540 } 4541 4542 fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { 4543 const num_limbs = math.cast(usize, int.getNumWords()) orelse return error.OutOfMemory; 4544 var aps_int = int; 4545 const is_negative = int.isSigned() and int.isNegative(); 4546 if (is_negative) aps_int = aps_int.negate(); 4547 defer if (is_negative) { 4548 aps_int.free(); 4549 }; 4550 4551 const limbs = try c.arena.alloc(math.big.Limb, num_limbs); 4552 defer c.arena.free(limbs); 4553 4554 const data = aps_int.getRawData(); 4555 switch (@sizeOf(math.big.Limb)) { 4556 8 => { 4557 var i: usize = 0; 4558 while (i < num_limbs) : (i += 1) { 4559 limbs[i] = data[i]; 4560 } 4561 }, 4562 4 => { 4563 var limb_i: usize = 0; 4564 var data_i: usize = 0; 4565 while (limb_i < num_limbs) : ({ 4566 limb_i += 2; 4567 data_i += 1; 4568 }) { 4569 limbs[limb_i] = @as(u32, @truncate(data[data_i])); 4570 limbs[limb_i + 1] = @as(u32, @truncate(data[data_i] >> 32)); 4571 } 4572 }, 4573 else => @compileError("unimplemented"), 4574 } 4575 4576 const big: math.big.int.Const = .{ .limbs = limbs, .positive = true }; 4577 const str = big.toStringAlloc(c.arena, 10, .lower) catch |err| switch (err) { 4578 error.OutOfMemory => return error.OutOfMemory, 4579 }; 4580 const res = try Tag.integer_literal.create(c.arena, str); 4581 if (is_negative) return Tag.negate.create(c.arena, res); 4582 return res; 4583 } 4584 4585 fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node { 4586 const fmt_s = switch (@typeInfo(@TypeOf(num))) { 4587 .Int, .ComptimeInt => "{d}", 4588 else => "{s}", 4589 }; 4590 const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num}); 4591 if (num_kind == .float) 4592 return Tag.float_literal.create(c.arena, str) 4593 else 4594 return Tag.integer_literal.create(c.arena, str); 4595 } 4596 4597 fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node { 4598 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); 4599 defer fn_params.deinit(); 4600 4601 for (proto_alias.data.params) |param| { 4602 const param_name = param.name orelse 4603 try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()}); 4604 4605 try fn_params.append(.{ 4606 .name = param_name, 4607 .type = param.type, 4608 .is_noalias = param.is_noalias, 4609 }); 4610 } 4611 4612 const init = if (ref.castTag(.var_decl)) |v| 4613 v.data.init.? 4614 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| 4615 v.data.init 4616 else 4617 unreachable; 4618 4619 const unwrap_expr = try Tag.unwrap.create(c.arena, init); 4620 const args = try c.arena.alloc(Node, fn_params.items.len); 4621 for (fn_params.items, 0..) |param, i| { 4622 args[i] = try Tag.identifier.create(c.arena, param.name.?); 4623 } 4624 const call_expr = try Tag.call.create(c.arena, .{ 4625 .lhs = unwrap_expr, 4626 .args = args, 4627 }); 4628 const return_expr = try Tag.@"return".create(c.arena, call_expr); 4629 const block = try Tag.block_single.create(c.arena, return_expr); 4630 4631 return Tag.pub_inline_fn.create(c.arena, .{ 4632 .name = name, 4633 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), 4634 .return_type = proto_alias.data.return_type, 4635 .body = block, 4636 }); 4637 } 4638 4639 fn transCreateNodeShiftOp( 4640 c: *Context, 4641 scope: *Scope, 4642 stmt: *const clang.BinaryOperator, 4643 op: Tag, 4644 used: ResultUsed, 4645 ) !Node { 4646 std.debug.assert(op == .shl or op == .shr); 4647 4648 const lhs_expr = stmt.getLHS(); 4649 const rhs_expr = stmt.getRHS(); 4650 // lhs >> @as(u5, rh) 4651 4652 const lhs = try transExpr(c, scope, lhs_expr, .used); 4653 4654 const rhs = try transExprCoercing(c, scope, rhs_expr, .used); 4655 const rhs_casted = try Tag.int_cast.create(c.arena, rhs); 4656 4657 return transCreateNodeInfixOp(c, op, lhs, rhs_casted, used); 4658 } 4659 4660 fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { 4661 switch (ty.getTypeClass()) { 4662 .Builtin => { 4663 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 4664 return Tag.type.create(c.arena, switch (builtin_ty.getKind()) { 4665 .Void => "anyopaque", 4666 .Bool => "bool", 4667 .Char_U, .UChar, .Char_S, .Char8 => "u8", 4668 .SChar => "i8", 4669 .UShort => "c_ushort", 4670 .UInt => "c_uint", 4671 .ULong => "c_ulong", 4672 .ULongLong => "c_ulonglong", 4673 .Short => "c_short", 4674 .Int => "c_int", 4675 .Long => "c_long", 4676 .LongLong => "c_longlong", 4677 .UInt128 => "u128", 4678 .Int128 => "i128", 4679 .Float => "f32", 4680 .Double => "f64", 4681 .Float128 => "f128", 4682 .Float16 => "f16", 4683 .LongDouble => "c_longdouble", 4684 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), 4685 }); 4686 }, 4687 .FunctionProto => { 4688 const fn_proto_ty = @as(*const clang.FunctionProtoType, @ptrCast(ty)); 4689 const fn_proto = try transFnProto(c, null, fn_proto_ty, source_loc, null, false); 4690 return Node.initPayload(&fn_proto.base); 4691 }, 4692 .FunctionNoProto => { 4693 const fn_no_proto_ty = @as(*const clang.FunctionType, @ptrCast(ty)); 4694 const fn_proto = try transFnNoProto(c, fn_no_proto_ty, source_loc, null, false); 4695 return Node.initPayload(&fn_proto.base); 4696 }, 4697 .Paren => { 4698 const paren_ty = @as(*const clang.ParenType, @ptrCast(ty)); 4699 return transQualType(c, scope, paren_ty.getInnerType(), source_loc); 4700 }, 4701 .Pointer => { 4702 const child_qt = ty.getPointeeType(); 4703 const is_fn_proto = qualTypeChildIsFnProto(child_qt); 4704 const is_const = is_fn_proto or child_qt.isConstQualified(); 4705 const is_volatile = child_qt.isVolatileQualified(); 4706 const elem_type = try transQualType(c, scope, child_qt, source_loc); 4707 const ptr_info = .{ 4708 .is_const = is_const, 4709 .is_volatile = is_volatile, 4710 .elem_type = elem_type, 4711 }; 4712 if (is_fn_proto or 4713 typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or 4714 qualTypeWasDemotedToOpaque(c, child_qt)) 4715 { 4716 const ptr = try Tag.single_pointer.create(c.arena, ptr_info); 4717 return Tag.optional_type.create(c.arena, ptr); 4718 } 4719 4720 return Tag.c_pointer.create(c.arena, ptr_info); 4721 }, 4722 .ConstantArray => { 4723 const const_arr_ty = @as(*const clang.ConstantArrayType, @ptrCast(ty)); 4724 4725 const size_ap_int = const_arr_ty.getSize(); 4726 const size = size_ap_int.getLimitedValue(usize); 4727 const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc); 4728 4729 return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); 4730 }, 4731 .IncompleteArray => { 4732 const incomplete_array_ty = @as(*const clang.IncompleteArrayType, @ptrCast(ty)); 4733 4734 const child_qt = incomplete_array_ty.getElementType(); 4735 const is_const = child_qt.isConstQualified(); 4736 const is_volatile = child_qt.isVolatileQualified(); 4737 const elem_type = try transQualType(c, scope, child_qt, source_loc); 4738 4739 return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); 4740 }, 4741 .Typedef => { 4742 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 4743 4744 const typedef_decl = typedef_ty.getDecl(); 4745 var trans_scope = scope; 4746 if (@as(*const clang.Decl, @ptrCast(typedef_decl)).castToNamedDecl()) |named_decl| { 4747 const decl_name = try c.str(named_decl.getName_bytes_begin()); 4748 if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base; 4749 if (builtin_typedef_map.get(decl_name)) |builtin| return Tag.type.create(c.arena, builtin); 4750 } 4751 try transTypeDef(c, trans_scope, typedef_decl); 4752 const name = c.decl_table.get(@intFromPtr(typedef_decl.getCanonicalDecl())).?; 4753 return Tag.identifier.create(c.arena, name); 4754 }, 4755 .Record => { 4756 const record_ty = @as(*const clang.RecordType, @ptrCast(ty)); 4757 4758 const record_decl = record_ty.getDecl(); 4759 var trans_scope = scope; 4760 if (@as(*const clang.Decl, @ptrCast(record_decl)).castToNamedDecl()) |named_decl| { 4761 const decl_name = try c.str(named_decl.getName_bytes_begin()); 4762 if (c.weak_global_names.contains(decl_name)) trans_scope = &c.global_scope.base; 4763 } 4764 try transRecordDecl(c, trans_scope, record_decl); 4765 const name = c.decl_table.get(@intFromPtr(record_decl.getCanonicalDecl())).?; 4766 return Tag.identifier.create(c.arena, name); 4767 }, 4768 .Enum => { 4769 const enum_ty = @as(*const clang.EnumType, @ptrCast(ty)); 4770 4771 const enum_decl = enum_ty.getDecl(); 4772 var trans_scope = scope; 4773 if (@as(*const clang.Decl, @ptrCast(enum_decl)).castToNamedDecl()) |named_decl| { 4774 const decl_name = try c.str(named_decl.getName_bytes_begin()); 4775 if (c.weak_global_names.contains(decl_name)) trans_scope = &c.global_scope.base; 4776 } 4777 try transEnumDecl(c, trans_scope, enum_decl); 4778 const name = c.decl_table.get(@intFromPtr(enum_decl.getCanonicalDecl())).?; 4779 return Tag.identifier.create(c.arena, name); 4780 }, 4781 .Elaborated => { 4782 const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty)); 4783 return transQualType(c, scope, elaborated_ty.getNamedType(), source_loc); 4784 }, 4785 .Decayed => { 4786 const decayed_ty = @as(*const clang.DecayedType, @ptrCast(ty)); 4787 return transQualType(c, scope, decayed_ty.getDecayedType(), source_loc); 4788 }, 4789 .Attributed => { 4790 const attributed_ty = @as(*const clang.AttributedType, @ptrCast(ty)); 4791 return transQualType(c, scope, attributed_ty.getEquivalentType(), source_loc); 4792 }, 4793 .MacroQualified => { 4794 const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(ty)); 4795 return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc); 4796 }, 4797 .TypeOf => { 4798 const typeof_ty = @as(*const clang.TypeOfType, @ptrCast(ty)); 4799 return transQualType(c, scope, typeof_ty.getUnmodifiedType(), source_loc); 4800 }, 4801 .TypeOfExpr => { 4802 const typeofexpr_ty = @as(*const clang.TypeOfExprType, @ptrCast(ty)); 4803 const underlying_expr = transExpr(c, scope, typeofexpr_ty.getUnderlyingExpr(), .used) catch |err| switch (err) { 4804 error.UnsupportedTranslation => { 4805 return fail(c, error.UnsupportedType, source_loc, "unsupported underlying expression for TypeOfExpr", .{}); 4806 }, 4807 else => |e| return e, 4808 }; 4809 return Tag.typeof.create(c.arena, underlying_expr); 4810 }, 4811 .Vector => { 4812 const vector_ty = @as(*const clang.VectorType, @ptrCast(ty)); 4813 const num_elements = vector_ty.getNumElements(); 4814 const element_qt = vector_ty.getElementType(); 4815 return Tag.vector.create(c.arena, .{ 4816 .lhs = try transCreateNodeNumber(c, num_elements, .int), 4817 .rhs = try transQualType(c, scope, element_qt, source_loc), 4818 }); 4819 }, 4820 .BitInt, .ExtVector => { 4821 const type_name = try c.str(ty.getTypeClassName()); 4822 return fail(c, error.UnsupportedType, source_loc, "TODO implement translation of type: '{s}'", .{type_name}); 4823 }, 4824 else => { 4825 const type_name = try c.str(ty.getTypeClassName()); 4826 return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); 4827 }, 4828 } 4829 } 4830 4831 fn qualTypeWasDemotedToOpaque(c: *Context, qt: clang.QualType) bool { 4832 const ty = qt.getTypePtr(); 4833 switch (qt.getTypeClass()) { 4834 .Typedef => { 4835 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 4836 4837 const typedef_decl = typedef_ty.getDecl(); 4838 const underlying_type = typedef_decl.getUnderlyingType(); 4839 return qualTypeWasDemotedToOpaque(c, underlying_type); 4840 }, 4841 .Record => { 4842 const record_ty = @as(*const clang.RecordType, @ptrCast(ty)); 4843 4844 const record_decl = record_ty.getDecl(); 4845 const canonical = @intFromPtr(record_decl.getCanonicalDecl()); 4846 if (c.opaque_demotes.contains(canonical)) return true; 4847 4848 // check all childern for opaque types. 4849 var it = record_decl.field_begin(); 4850 const end_it = record_decl.field_end(); 4851 while (it.neq(end_it)) : (it = it.next()) { 4852 const field_decl = it.deref(); 4853 if (qualTypeWasDemotedToOpaque(c, field_decl.getType())) return true; 4854 } 4855 return false; 4856 }, 4857 .Enum => { 4858 const enum_ty = @as(*const clang.EnumType, @ptrCast(ty)); 4859 4860 const enum_decl = enum_ty.getDecl(); 4861 const canonical = @intFromPtr(enum_decl.getCanonicalDecl()); 4862 return c.opaque_demotes.contains(canonical); 4863 }, 4864 .Elaborated => { 4865 const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty)); 4866 return qualTypeWasDemotedToOpaque(c, elaborated_ty.getNamedType()); 4867 }, 4868 .Decayed => { 4869 const decayed_ty = @as(*const clang.DecayedType, @ptrCast(ty)); 4870 return qualTypeWasDemotedToOpaque(c, decayed_ty.getDecayedType()); 4871 }, 4872 .Attributed => { 4873 const attributed_ty = @as(*const clang.AttributedType, @ptrCast(ty)); 4874 return qualTypeWasDemotedToOpaque(c, attributed_ty.getEquivalentType()); 4875 }, 4876 .MacroQualified => { 4877 const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(ty)); 4878 return qualTypeWasDemotedToOpaque(c, macroqualified_ty.getModifiedType()); 4879 }, 4880 else => return false, 4881 } 4882 } 4883 4884 fn isAnyopaque(qt: clang.QualType) bool { 4885 const ty = qt.getTypePtr(); 4886 switch (ty.getTypeClass()) { 4887 .Builtin => { 4888 const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty)); 4889 return builtin_ty.getKind() == .Void; 4890 }, 4891 .Typedef => { 4892 const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty)); 4893 const typedef_decl = typedef_ty.getDecl(); 4894 return isAnyopaque(typedef_decl.getUnderlyingType()); 4895 }, 4896 .Elaborated => { 4897 const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty)); 4898 return isAnyopaque(elaborated_ty.getNamedType().getCanonicalType()); 4899 }, 4900 .Decayed => { 4901 const decayed_ty = @as(*const clang.DecayedType, @ptrCast(ty)); 4902 return isAnyopaque(decayed_ty.getDecayedType().getCanonicalType()); 4903 }, 4904 .Attributed => { 4905 const attributed_ty = @as(*const clang.AttributedType, @ptrCast(ty)); 4906 return isAnyopaque(attributed_ty.getEquivalentType().getCanonicalType()); 4907 }, 4908 .MacroQualified => { 4909 const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(ty)); 4910 return isAnyopaque(macroqualified_ty.getModifiedType().getCanonicalType()); 4911 }, 4912 else => return false, 4913 } 4914 } 4915 4916 const FnDeclContext = struct { 4917 fn_name: []const u8, 4918 has_body: bool, 4919 storage_class: clang.StorageClass, 4920 is_always_inline: bool, 4921 is_export: bool, 4922 }; 4923 4924 fn transCC( 4925 c: *Context, 4926 fn_ty: *const clang.FunctionType, 4927 source_loc: clang.SourceLocation, 4928 ) !CallingConvention { 4929 const clang_cc = fn_ty.getCallConv(); 4930 switch (clang_cc) { 4931 .C => return CallingConvention.C, 4932 .X86StdCall => return CallingConvention.Stdcall, 4933 .X86FastCall => return CallingConvention.Fastcall, 4934 .X86VectorCall, .AArch64VectorCall => return CallingConvention.Vectorcall, 4935 .X86ThisCall => return CallingConvention.Thiscall, 4936 .AAPCS => return CallingConvention.AAPCS, 4937 .AAPCS_VFP => return CallingConvention.AAPCSVFP, 4938 .X86_64SysV => return CallingConvention.SysV, 4939 else => return fail( 4940 c, 4941 error.UnsupportedType, 4942 source_loc, 4943 "unsupported calling convention: {s}", 4944 .{@tagName(clang_cc)}, 4945 ), 4946 } 4947 } 4948 4949 fn transFnProto( 4950 c: *Context, 4951 fn_decl: ?*const clang.FunctionDecl, 4952 fn_proto_ty: *const clang.FunctionProtoType, 4953 source_loc: clang.SourceLocation, 4954 fn_decl_context: ?FnDeclContext, 4955 is_pub: bool, 4956 ) !*ast.Payload.Func { 4957 const fn_ty = @as(*const clang.FunctionType, @ptrCast(fn_proto_ty)); 4958 const cc = try transCC(c, fn_ty, source_loc); 4959 const is_var_args = fn_proto_ty.isVariadic(); 4960 return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); 4961 } 4962 4963 fn transFnNoProto( 4964 c: *Context, 4965 fn_ty: *const clang.FunctionType, 4966 source_loc: clang.SourceLocation, 4967 fn_decl_context: ?FnDeclContext, 4968 is_pub: bool, 4969 ) !*ast.Payload.Func { 4970 const cc = try transCC(c, fn_ty, source_loc); 4971 const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static and !ctx.is_always_inline) else true; 4972 return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); 4973 } 4974 4975 fn finishTransFnProto( 4976 c: *Context, 4977 fn_decl: ?*const clang.FunctionDecl, 4978 fn_proto_ty: ?*const clang.FunctionProtoType, 4979 fn_ty: *const clang.FunctionType, 4980 source_loc: clang.SourceLocation, 4981 fn_decl_context: ?FnDeclContext, 4982 is_var_args: bool, 4983 cc: CallingConvention, 4984 is_pub: bool, 4985 ) !*ast.Payload.Func { 4986 const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; 4987 const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false; 4988 const is_inline = if (fn_decl_context) |ctx| ctx.is_always_inline else false; 4989 const scope = &c.global_scope.base; 4990 4991 const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0; 4992 var fn_params = try std.ArrayList(ast.Payload.Param).initCapacity(c.gpa, param_count); 4993 defer fn_params.deinit(); 4994 4995 var i: usize = 0; 4996 while (i < param_count) : (i += 1) { 4997 const param_qt = fn_proto_ty.?.getParamType(@as(c_uint, @intCast(i))); 4998 const is_noalias = param_qt.isRestrictQualified(); 4999 5000 const param_name: ?[]const u8 = 5001 if (fn_decl) |decl| 5002 blk: { 5003 const param = decl.getParamDecl(@as(c_uint, @intCast(i))); 5004 const param_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(param)).getName_bytes_begin()); 5005 if (param_name.len < 1) 5006 break :blk null; 5007 5008 break :blk param_name; 5009 } else null; 5010 const type_node = try transQualType(c, scope, param_qt, source_loc); 5011 5012 fn_params.addOneAssumeCapacity().* = .{ 5013 .is_noalias = is_noalias, 5014 .name = param_name, 5015 .type = type_node, 5016 }; 5017 } 5018 5019 const linksection_string = blk: { 5020 if (fn_decl) |decl| { 5021 var str_len: usize = undefined; 5022 if (decl.getSectionAttribute(&str_len)) |str_ptr| { 5023 break :blk str_ptr[0..str_len]; 5024 } 5025 } 5026 break :blk null; 5027 }; 5028 5029 const alignment = if (fn_decl) |decl| ClangAlignment.forFunc(c, decl).zigAlignment() else null; 5030 5031 const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .C) null else cc; 5032 5033 const return_type_node = blk: { 5034 if (fn_ty.getNoReturnAttr()) { 5035 break :blk Tag.noreturn_type.init(); 5036 } else { 5037 const return_qt = fn_ty.getReturnType(); 5038 if (isAnyopaque(return_qt)) { 5039 // convert primitive anyopaque to actual void (only for return type) 5040 break :blk Tag.void_type.init(); 5041 } else { 5042 break :blk transQualType(c, scope, return_qt, source_loc) catch |err| switch (err) { 5043 error.UnsupportedType => { 5044 try warn(c, scope, source_loc, "unsupported function proto return type", .{}); 5045 return err; 5046 }, 5047 error.OutOfMemory => |e| return e, 5048 }; 5049 } 5050 } 5051 }; 5052 const name: ?[]const u8 = if (fn_decl_context) |ctx| ctx.fn_name else null; 5053 const payload = try c.arena.create(ast.Payload.Func); 5054 payload.* = .{ 5055 .base = .{ .tag = .func }, 5056 .data = .{ 5057 .is_pub = is_pub, 5058 .is_extern = is_extern, 5059 .is_export = is_export, 5060 .is_inline = is_inline, 5061 .is_var_args = is_var_args, 5062 .name = name, 5063 .linksection_string = linksection_string, 5064 .explicit_callconv = explicit_callconv, 5065 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), 5066 .return_type = return_type_node, 5067 .body = null, 5068 .alignment = alignment, 5069 }, 5070 }; 5071 return payload; 5072 } 5073 5074 fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { 5075 const str = try c.locStr(loc); 5076 const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, .{str} ++ args); 5077 try scope.appendNode(try Tag.warning.create(c.arena, value)); 5078 } 5079 5080 fn fail( 5081 c: *Context, 5082 err: anytype, 5083 source_loc: clang.SourceLocation, 5084 comptime format: []const u8, 5085 args: anytype, 5086 ) (@TypeOf(err) || error{OutOfMemory}) { 5087 try warn(c, &c.global_scope.base, source_loc, format, args); 5088 return err; 5089 } 5090 5091 pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) Error!void { 5092 // location 5093 // pub const name = @compileError(msg); 5094 const fail_msg = try std.fmt.allocPrint(c.arena, format, args); 5095 try addTopLevelDecl(c, name, try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg })); 5096 const str = try c.locStr(loc); 5097 const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{str}); 5098 try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment)); 5099 } 5100 5101 const MacroCtx = struct { 5102 source: []const u8, 5103 list: []const CToken, 5104 i: usize = 0, 5105 loc: clang.SourceLocation, 5106 name: []const u8, 5107 5108 fn peek(self: *MacroCtx) ?CToken.Id { 5109 if (self.i >= self.list.len) return null; 5110 return self.list[self.i + 1].id; 5111 } 5112 5113 fn next(self: *MacroCtx) ?CToken.Id { 5114 if (self.i >= self.list.len) return null; 5115 self.i += 1; 5116 return self.list[self.i].id; 5117 } 5118 5119 fn skip(self: *MacroCtx, c: *Context, expected_id: CToken.Id) ParseError!void { 5120 const next_id = self.next().?; 5121 if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) { 5122 try self.fail( 5123 c, 5124 "unable to translate C expr: expected '{s}' instead got '{s}'", 5125 .{ expected_id.symbol(), next_id.symbol() }, 5126 ); 5127 return error.ParseError; 5128 } 5129 } 5130 5131 fn slice(self: *MacroCtx) []const u8 { 5132 const tok = self.list[self.i]; 5133 return self.source[tok.start..tok.end]; 5134 } 5135 5136 fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void { 5137 return failDecl(c, self.loc, self.name, fmt, args); 5138 } 5139 5140 fn makeSlicer(self: *const MacroCtx) MacroSlicer { 5141 return .{ .source = self.source, .tokens = self.list }; 5142 } 5143 5144 const MacroTranslateError = union(enum) { 5145 undefined_identifier: []const u8, 5146 invalid_arg_usage: []const u8, 5147 }; 5148 5149 fn checkTranslatableMacro(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?MacroTranslateError { 5150 const slicer = self.makeSlicer(); 5151 var last_is_type_kw = false; 5152 var i: usize = 1; // index 0 is the macro name 5153 while (i < self.list.len) : (i += 1) { 5154 const token = self.list[i]; 5155 switch (token.id) { 5156 .period, .arrow => i += 1, // skip next token since field identifiers can be unknown 5157 .keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) { 5158 last_is_type_kw = true; 5159 continue; 5160 }, 5161 .identifier, .extended_identifier => { 5162 const identifier = slicer.slice(token); 5163 const is_param = for (params) |param| { 5164 if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true; 5165 } else false; 5166 if (is_param and last_is_type_kw) { 5167 return .{ .invalid_arg_usage = identifier }; 5168 } 5169 if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) { 5170 return .{ .undefined_identifier = identifier }; 5171 } 5172 }, 5173 else => {}, 5174 } 5175 last_is_type_kw = false; 5176 } 5177 return null; 5178 } 5179 }; 5180 5181 fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) ![]const u8 { 5182 const begin_loc = macro.getSourceRange_getBegin(); 5183 const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit); 5184 5185 const begin_c = c.source_manager.getCharacterData(begin_loc); 5186 const end_c = c.source_manager.getCharacterData(end_loc); 5187 const slice_len = @intFromPtr(end_c) - @intFromPtr(begin_c); 5188 5189 var comp = aro.Compilation.init(c.gpa); 5190 defer comp.deinit(); 5191 const result = comp.addSourceFromBuffer("", begin_c[0..slice_len]) catch return error.OutOfMemory; 5192 5193 return c.arena.dupe(u8, result.buf); 5194 } 5195 5196 fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { 5197 // TODO if we see #undef, delete it from the table 5198 var it = unit.getLocalPreprocessingEntities_begin(); 5199 const it_end = unit.getLocalPreprocessingEntities_end(); 5200 var tok_list = std.ArrayList(CToken).init(c.gpa); 5201 defer tok_list.deinit(); 5202 const scope = c.global_scope; 5203 5204 while (it.I != it_end.I) : (it.I += 1) { 5205 const entity = it.deref(); 5206 tok_list.items.len = 0; 5207 switch (entity.getKind()) { 5208 .MacroDefinitionKind => { 5209 const macro = @as(*clang.MacroDefinitionRecord, @ptrCast(entity)); 5210 const raw_name = macro.getName_getNameStart(); 5211 const begin_loc = macro.getSourceRange_getBegin(); 5212 5213 const name = try c.str(raw_name); 5214 if (scope.containsNow(name)) { 5215 continue; 5216 } 5217 5218 const source = try getMacroText(unit, c, macro); 5219 5220 try common.tokenizeMacro(source, &tok_list); 5221 5222 var macro_ctx = MacroCtx{ 5223 .source = source, 5224 .list = tok_list.items, 5225 .name = name, 5226 .loc = begin_loc, 5227 }; 5228 assert(mem.eql(u8, macro_ctx.slice(), name)); 5229 5230 var macro_fn = false; 5231 switch (macro_ctx.peek().?) { 5232 .identifier, .extended_identifier => { 5233 // if it equals itself, ignore. for example, from stdio.h: 5234 // #define stdin stdin 5235 const tok = macro_ctx.list[1]; 5236 if (mem.eql(u8, name, source[tok.start..tok.end])) { 5237 assert(!c.global_names.contains(source[tok.start..tok.end])); 5238 continue; 5239 } 5240 }, 5241 .nl, .eof => { 5242 // this means it is a macro without a value 5243 // We define it as an empty string so that it can still be used with ++ 5244 const str_node = try Tag.string_literal.create(c.arena, "\"\""); 5245 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = name, .init = str_node }); 5246 try c.global_scope.macro_table.put(name, var_decl); 5247 try c.global_scope.blank_macros.put(name, {}); 5248 continue; 5249 }, 5250 .l_paren => { 5251 // if the name is immediately followed by a '(' then it is a function 5252 macro_fn = macro_ctx.list[0].end == macro_ctx.list[1].start; 5253 }, 5254 else => {}, 5255 } 5256 5257 (if (macro_fn) 5258 transMacroFnDefine(c, ¯o_ctx) 5259 else 5260 transMacroDefine(c, ¯o_ctx)) catch |err| switch (err) { 5261 error.ParseError => continue, 5262 error.OutOfMemory => |e| return e, 5263 }; 5264 }, 5265 else => {}, 5266 } 5267 } 5268 } 5269 5270 fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { 5271 const scope = &c.global_scope.base; 5272 5273 if (m.checkTranslatableMacro(scope, &.{})) |err| switch (err) { 5274 .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}), 5275 .invalid_arg_usage => unreachable, // no args 5276 }; 5277 5278 // Check if the macro only uses other blank macros. 5279 while (true) { 5280 switch (m.peek().?) { 5281 .identifier, .extended_identifier => { 5282 const tok = m.list[m.i + 1]; 5283 const slice = m.source[tok.start..tok.end]; 5284 if (c.global_scope.blank_macros.contains(slice)) { 5285 m.i += 1; 5286 continue; 5287 } 5288 }, 5289 .eof, .nl => { 5290 try c.global_scope.blank_macros.put(m.name, {}); 5291 const init_node = try Tag.string_literal.create(c.arena, "\"\""); 5292 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); 5293 try c.global_scope.macro_table.put(m.name, var_decl); 5294 return; 5295 }, 5296 else => {}, 5297 } 5298 break; 5299 } 5300 5301 const init_node = try parseCExpr(c, m, scope); 5302 const last = m.next().?; 5303 if (last != .eof and last != .nl) 5304 return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()}); 5305 5306 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); 5307 try c.global_scope.macro_table.put(m.name, var_decl); 5308 } 5309 5310 fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { 5311 const macro_slicer = m.makeSlicer(); 5312 if (try c.pattern_list.match(c.gpa, macro_slicer)) |pattern| { 5313 const decl = try Tag.pub_var_simple.create(c.arena, .{ 5314 .name = m.name, 5315 .init = try Tag.helpers_macro.create(c.arena, pattern.impl), 5316 }); 5317 try c.global_scope.macro_table.put(m.name, decl); 5318 return; 5319 } 5320 5321 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false); 5322 defer block_scope.deinit(); 5323 const scope = &block_scope.base; 5324 5325 try m.skip(c, .l_paren); 5326 5327 var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); 5328 defer fn_params.deinit(); 5329 5330 while (true) { 5331 switch (m.peek().?) { 5332 .identifier, .extended_identifier => _ = m.next(), 5333 else => break, 5334 } 5335 5336 const mangled_name = try block_scope.makeMangledName(c, m.slice()); 5337 try fn_params.append(.{ 5338 .is_noalias = false, 5339 .name = mangled_name, 5340 .type = Tag.@"anytype".init(), 5341 }); 5342 try block_scope.discardVariable(c, mangled_name); 5343 if (m.peek().? != .comma) break; 5344 _ = m.next(); 5345 } 5346 5347 try m.skip(c, .r_paren); 5348 5349 if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) { 5350 .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}), 5351 .invalid_arg_usage => |ident| return m.fail(c, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}), 5352 }; 5353 5354 const expr = try parseCExpr(c, m, scope); 5355 const last = m.next().?; 5356 if (last != .eof and last != .nl) 5357 return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()}); 5358 5359 const typeof_arg = if (expr.castTag(.block)) |some| blk: { 5360 const stmts = some.data.stmts; 5361 const blk_last = stmts[stmts.len - 1]; 5362 const br = blk_last.castTag(.break_val).?; 5363 break :blk br.data.val; 5364 } else expr; 5365 5366 const return_type = if (typeof_arg.castTag(.helpers_cast) orelse typeof_arg.castTag(.std_mem_zeroinit)) |some| 5367 some.data.lhs 5368 else if (typeof_arg.castTag(.std_mem_zeroes)) |some| 5369 some.data 5370 else 5371 try Tag.typeof.create(c.arena, typeof_arg); 5372 5373 const return_expr = try Tag.@"return".create(c.arena, expr); 5374 try block_scope.statements.append(return_expr); 5375 5376 const fn_decl = try Tag.pub_inline_fn.create(c.arena, .{ 5377 .name = m.name, 5378 .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), 5379 .return_type = return_type, 5380 .body = try block_scope.complete(c), 5381 }); 5382 try c.global_scope.macro_table.put(m.name, fn_decl); 5383 } 5384 5385 const ParseError = Error || error{ParseError}; 5386 5387 fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5388 // TODO parseCAssignExpr here 5389 var block_scope = try Scope.Block.init(c, scope, true); 5390 defer block_scope.deinit(); 5391 5392 const node = try parseCCondExpr(c, m, &block_scope.base); 5393 if (m.next().? != .comma) { 5394 m.i -= 1; 5395 return node; 5396 } 5397 5398 var last = node; 5399 while (true) { 5400 // suppress result 5401 const ignore = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = last }); 5402 try block_scope.statements.append(ignore); 5403 5404 last = try parseCCondExpr(c, m, &block_scope.base); 5405 if (m.next().? != .comma) { 5406 m.i -= 1; 5407 break; 5408 } 5409 } 5410 5411 const break_node = try Tag.break_val.create(c.arena, .{ 5412 .label = block_scope.label, 5413 .val = last, 5414 }); 5415 try block_scope.statements.append(break_node); 5416 return try block_scope.complete(c); 5417 } 5418 5419 fn parseCNumLit(ctx: *Context, m: *MacroCtx) ParseError!Node { 5420 const lit_bytes = m.slice(); 5421 var bytes = try std.ArrayListUnmanaged(u8).initCapacity(ctx.arena, lit_bytes.len + 3); 5422 5423 const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes); 5424 switch (prefix) { 5425 .binary => bytes.appendSliceAssumeCapacity("0b"), 5426 .octal => bytes.appendSliceAssumeCapacity("0o"), 5427 .hex => bytes.appendSliceAssumeCapacity("0x"), 5428 .decimal => {}, 5429 } 5430 5431 const after_prefix = lit_bytes[prefix.stringLen()..]; 5432 const after_int = for (after_prefix, 0..) |c, i| switch (c) { 5433 '.' => { 5434 if (i == 0) { 5435 bytes.appendAssumeCapacity('0'); 5436 } 5437 break after_prefix[i..]; 5438 }, 5439 'e', 'E' => { 5440 if (prefix != .hex) break after_prefix[i..]; 5441 bytes.appendAssumeCapacity(c); 5442 }, 5443 'p', 'P' => break after_prefix[i..], 5444 '0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => { 5445 if (!prefix.digitAllowed(c)) break after_prefix[i..]; 5446 bytes.appendAssumeCapacity(c); 5447 }, 5448 '\'' => { 5449 bytes.appendAssumeCapacity('_'); 5450 }, 5451 else => break after_prefix[i..], 5452 } else ""; 5453 5454 const after_frac = frac: { 5455 if (after_int.len == 0 or after_int[0] != '.') break :frac after_int; 5456 bytes.appendAssumeCapacity('.'); 5457 for (after_int[1..], 1..) |c, i| { 5458 if (c == '\'') { 5459 bytes.appendAssumeCapacity('_'); 5460 continue; 5461 } 5462 if (!prefix.digitAllowed(c)) break :frac after_int[i..]; 5463 bytes.appendAssumeCapacity(c); 5464 } 5465 break :frac ""; 5466 }; 5467 5468 const suffix_str = exponent: { 5469 if (after_frac.len == 0) break :exponent after_frac; 5470 switch (after_frac[0]) { 5471 'e', 'E' => {}, 5472 'p', 'P' => if (prefix != .hex) break :exponent after_frac, 5473 else => break :exponent after_frac, 5474 } 5475 bytes.appendAssumeCapacity(after_frac[0]); 5476 for (after_frac[1..], 1..) |c, i| switch (c) { 5477 '+', '-', '0'...'9' => { 5478 bytes.appendAssumeCapacity(c); 5479 }, 5480 '\'' => { 5481 bytes.appendAssumeCapacity('_'); 5482 }, 5483 else => break :exponent after_frac[i..], 5484 }; 5485 break :exponent ""; 5486 }; 5487 5488 const is_float = after_int.len != suffix_str.len; 5489 const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse { 5490 try m.fail(ctx, "invalid number suffix: '{s}'", .{suffix_str}); 5491 return error.ParseError; 5492 }; 5493 if (suffix.isImaginary()) { 5494 try m.fail(ctx, "TODO: imaginary literals", .{}); 5495 return error.ParseError; 5496 } 5497 if (suffix.isBitInt()) { 5498 try m.fail(ctx, "TODO: _BitInt literals", .{}); 5499 return error.ParseError; 5500 } 5501 5502 if (is_float) { 5503 const type_node = try Tag.type.create(ctx.arena, switch (suffix) { 5504 .F16 => "f16", 5505 .F => "f32", 5506 .None => "f64", 5507 .L => "c_longdouble", 5508 .W => "f80", 5509 .Q, .F128 => "f128", 5510 else => unreachable, 5511 }); 5512 const rhs = try Tag.float_literal.create(ctx.arena, bytes.items); 5513 return Tag.as.create(ctx.arena, .{ .lhs = type_node, .rhs = rhs }); 5514 } else { 5515 const type_node = try Tag.type.create(ctx.arena, switch (suffix) { 5516 .None => "c_int", 5517 .U => "c_uint", 5518 .L => "c_long", 5519 .UL => "c_ulong", 5520 .LL => "c_longlong", 5521 .ULL => "c_ulonglong", 5522 else => unreachable, 5523 }); 5524 const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128); 5525 5526 // make the output less noisy by skipping promoteIntLiteral where 5527 // it's guaranteed to not be required because of C standard type constraints 5528 const guaranteed_to_fit = switch (suffix) { 5529 .None => math.cast(i16, value) != null, 5530 .U => math.cast(u16, value) != null, 5531 .L => math.cast(i32, value) != null, 5532 .UL => math.cast(u32, value) != null, 5533 .LL => math.cast(i64, value) != null, 5534 .ULL => math.cast(u64, value) != null, 5535 else => unreachable, 5536 }; 5537 5538 const literal_node = try Tag.integer_literal.create(ctx.arena, bytes.items); 5539 if (guaranteed_to_fit) { 5540 return Tag.as.create(ctx.arena, .{ .lhs = type_node, .rhs = literal_node }); 5541 } else { 5542 return Tag.helpers_promoteIntLiteral.create(ctx.arena, .{ 5543 .type = type_node, 5544 .value = literal_node, 5545 .base = try Tag.enum_literal.create(ctx.arena, @tagName(prefix)), 5546 }); 5547 } 5548 } 5549 } 5550 5551 fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { 5552 var source = m.slice(); 5553 for (source, 0..) |c, i| { 5554 if (c == '\"' or c == '\'') { 5555 source = source[i..]; 5556 break; 5557 } 5558 } 5559 for (source) |c| { 5560 if (c == '\\' or c == '\t') { 5561 break; 5562 } 5563 } else return source; 5564 var bytes = try ctx.arena.alloc(u8, source.len * 2); 5565 var state: enum { 5566 start, 5567 escape, 5568 hex, 5569 octal, 5570 } = .start; 5571 var i: usize = 0; 5572 var count: u8 = 0; 5573 var num: u8 = 0; 5574 for (source) |c| { 5575 switch (state) { 5576 .escape => { 5577 switch (c) { 5578 'n', 'r', 't', '\\', '\'', '\"' => { 5579 bytes[i] = c; 5580 }, 5581 '0'...'7' => { 5582 count += 1; 5583 num += c - '0'; 5584 state = .octal; 5585 bytes[i] = 'x'; 5586 }, 5587 'x' => { 5588 state = .hex; 5589 bytes[i] = 'x'; 5590 }, 5591 'a' => { 5592 bytes[i] = 'x'; 5593 i += 1; 5594 bytes[i] = '0'; 5595 i += 1; 5596 bytes[i] = '7'; 5597 }, 5598 'b' => { 5599 bytes[i] = 'x'; 5600 i += 1; 5601 bytes[i] = '0'; 5602 i += 1; 5603 bytes[i] = '8'; 5604 }, 5605 'f' => { 5606 bytes[i] = 'x'; 5607 i += 1; 5608 bytes[i] = '0'; 5609 i += 1; 5610 bytes[i] = 'C'; 5611 }, 5612 'v' => { 5613 bytes[i] = 'x'; 5614 i += 1; 5615 bytes[i] = '0'; 5616 i += 1; 5617 bytes[i] = 'B'; 5618 }, 5619 '?' => { 5620 i -= 1; 5621 bytes[i] = '?'; 5622 }, 5623 'u', 'U' => { 5624 try m.fail(ctx, "macro tokenizing failed: TODO unicode escape sequences", .{}); 5625 return error.ParseError; 5626 }, 5627 else => { 5628 try m.fail(ctx, "macro tokenizing failed: unknown escape sequence", .{}); 5629 return error.ParseError; 5630 }, 5631 } 5632 i += 1; 5633 if (state == .escape) 5634 state = .start; 5635 }, 5636 .start => { 5637 if (c == '\t') { 5638 bytes[i] = '\\'; 5639 i += 1; 5640 bytes[i] = 't'; 5641 i += 1; 5642 continue; 5643 } 5644 if (c == '\\') { 5645 state = .escape; 5646 } 5647 bytes[i] = c; 5648 i += 1; 5649 }, 5650 .hex => { 5651 switch (c) { 5652 '0'...'9' => { 5653 num = std.math.mul(u8, num, 16) catch { 5654 try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{}); 5655 return error.ParseError; 5656 }; 5657 num += c - '0'; 5658 }, 5659 'a'...'f' => { 5660 num = std.math.mul(u8, num, 16) catch { 5661 try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{}); 5662 return error.ParseError; 5663 }; 5664 num += c - 'a' + 10; 5665 }, 5666 'A'...'F' => { 5667 num = std.math.mul(u8, num, 16) catch { 5668 try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{}); 5669 return error.ParseError; 5670 }; 5671 num += c - 'A' + 10; 5672 }, 5673 else => { 5674 i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); 5675 num = 0; 5676 if (c == '\\') 5677 state = .escape 5678 else 5679 state = .start; 5680 bytes[i] = c; 5681 i += 1; 5682 }, 5683 } 5684 }, 5685 .octal => { 5686 const accept_digit = switch (c) { 5687 // The maximum length of a octal literal is 3 digits 5688 '0'...'7' => count < 3, 5689 else => false, 5690 }; 5691 5692 if (accept_digit) { 5693 count += 1; 5694 num = std.math.mul(u8, num, 8) catch { 5695 try m.fail(ctx, "macro tokenizing failed: octal literal overflowed", .{}); 5696 return error.ParseError; 5697 }; 5698 num += c - '0'; 5699 } else { 5700 i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); 5701 num = 0; 5702 count = 0; 5703 if (c == '\\') 5704 state = .escape 5705 else 5706 state = .start; 5707 bytes[i] = c; 5708 i += 1; 5709 } 5710 }, 5711 } 5712 } 5713 if (state == .hex or state == .octal) 5714 i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 }); 5715 return bytes[0..i]; 5716 } 5717 5718 /// non-ASCII characters (c > 127) are also treated as non-printable by fmtSliceEscapeLower. 5719 /// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape 5720 /// non-ASCII characters so that the Zig source we output will itself be UTF-8. 5721 fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 { 5722 const zigified = try zigifyEscapeSequences(ctx, m); 5723 if (std.unicode.utf8ValidateSlice(zigified)) return zigified; 5724 5725 const formatter = std.fmt.fmtSliceEscapeLower(zigified); 5726 const encoded_size = @as(usize, @intCast(std.fmt.count("{s}", .{formatter}))); 5727 const output = try ctx.arena.alloc(u8, encoded_size); 5728 return std.fmt.bufPrint(output, "{s}", .{formatter}) catch |err| switch (err) { 5729 error.NoSpaceLeft => unreachable, 5730 else => |e| return e, 5731 }; 5732 } 5733 5734 fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5735 const tok = m.next().?; 5736 const slice = m.slice(); 5737 switch (tok) { 5738 .char_literal, 5739 .char_literal_utf_8, 5740 .char_literal_utf_16, 5741 .char_literal_utf_32, 5742 .char_literal_wide, 5743 => { 5744 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { 5745 return Tag.char_literal.create(c.arena, try escapeUnprintables(c, m)); 5746 } else { 5747 const str = try std.fmt.allocPrint(c.arena, "0x{s}", .{std.fmt.fmtSliceHexLower(slice[1 .. slice.len - 1])}); 5748 return Tag.integer_literal.create(c.arena, str); 5749 } 5750 }, 5751 .string_literal, 5752 .string_literal_utf_16, 5753 .string_literal_utf_8, 5754 .string_literal_utf_32, 5755 .string_literal_wide, 5756 => { 5757 return Tag.string_literal.create(c.arena, try escapeUnprintables(c, m)); 5758 }, 5759 .pp_num => { 5760 return parseCNumLit(c, m); 5761 }, 5762 .identifier, .extended_identifier => { 5763 if (c.global_scope.blank_macros.contains(slice)) { 5764 return parseCPrimaryExpr(c, m, scope); 5765 } 5766 const mangled_name = scope.getAlias(slice); 5767 if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty); 5768 const identifier = try Tag.identifier.create(c.arena, mangled_name); 5769 scope.skipVariableDiscard(identifier.castTag(.identifier).?.data); 5770 return identifier; 5771 }, 5772 .l_paren => { 5773 const inner_node = try parseCExpr(c, m, scope); 5774 5775 try m.skip(c, .r_paren); 5776 return inner_node; 5777 }, 5778 else => { 5779 // for handling type macros (EVIL) 5780 // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList? 5781 m.i -= 1; 5782 if (try parseCTypeName(c, m, scope, true)) |type_name| { 5783 return type_name; 5784 } 5785 try m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{tok.symbol()}); 5786 return error.ParseError; 5787 }, 5788 } 5789 } 5790 5791 fn macroIntFromBool(c: *Context, node: Node) !Node { 5792 if (!isBoolRes(node)) { 5793 return node; 5794 } 5795 5796 return Tag.int_from_bool.create(c.arena, node); 5797 } 5798 5799 fn macroIntToBool(c: *Context, node: Node) !Node { 5800 if (isBoolRes(node)) { 5801 return node; 5802 } 5803 if (node.tag() == .string_literal) { 5804 // @intFromPtr(node) != 0 5805 const int_from_ptr = try Tag.int_from_ptr.create(c.arena, node); 5806 return Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() }); 5807 } 5808 // node != 0 5809 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); 5810 } 5811 5812 fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5813 const node = try parseCOrExpr(c, m, scope); 5814 if (m.peek().? != .question_mark) { 5815 return node; 5816 } 5817 _ = m.next(); 5818 5819 const then_body = try parseCOrExpr(c, m, scope); 5820 try m.skip(c, .colon); 5821 const else_body = try parseCCondExpr(c, m, scope); 5822 return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body }); 5823 } 5824 5825 fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5826 var node = try parseCAndExpr(c, m, scope); 5827 while (m.next().? == .pipe_pipe) { 5828 const lhs = try macroIntToBool(c, node); 5829 const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope)); 5830 node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5831 } 5832 m.i -= 1; 5833 return node; 5834 } 5835 5836 fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5837 var node = try parseCBitOrExpr(c, m, scope); 5838 while (m.next().? == .ampersand_ampersand) { 5839 const lhs = try macroIntToBool(c, node); 5840 const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope)); 5841 node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5842 } 5843 m.i -= 1; 5844 return node; 5845 } 5846 5847 fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5848 var node = try parseCBitXorExpr(c, m, scope); 5849 while (m.next().? == .pipe) { 5850 const lhs = try macroIntFromBool(c, node); 5851 const rhs = try macroIntFromBool(c, try parseCBitXorExpr(c, m, scope)); 5852 node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5853 } 5854 m.i -= 1; 5855 return node; 5856 } 5857 5858 fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5859 var node = try parseCBitAndExpr(c, m, scope); 5860 while (m.next().? == .caret) { 5861 const lhs = try macroIntFromBool(c, node); 5862 const rhs = try macroIntFromBool(c, try parseCBitAndExpr(c, m, scope)); 5863 node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5864 } 5865 m.i -= 1; 5866 return node; 5867 } 5868 5869 fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5870 var node = try parseCEqExpr(c, m, scope); 5871 while (m.next().? == .ampersand) { 5872 const lhs = try macroIntFromBool(c, node); 5873 const rhs = try macroIntFromBool(c, try parseCEqExpr(c, m, scope)); 5874 node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5875 } 5876 m.i -= 1; 5877 return node; 5878 } 5879 5880 fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5881 var node = try parseCRelExpr(c, m, scope); 5882 while (true) { 5883 switch (m.peek().?) { 5884 .bang_equal => { 5885 _ = m.next(); 5886 const lhs = try macroIntFromBool(c, node); 5887 const rhs = try macroIntFromBool(c, try parseCRelExpr(c, m, scope)); 5888 node = try Tag.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5889 }, 5890 .equal_equal => { 5891 _ = m.next(); 5892 const lhs = try macroIntFromBool(c, node); 5893 const rhs = try macroIntFromBool(c, try parseCRelExpr(c, m, scope)); 5894 node = try Tag.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5895 }, 5896 else => return node, 5897 } 5898 } 5899 } 5900 5901 fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5902 var node = try parseCShiftExpr(c, m, scope); 5903 while (true) { 5904 switch (m.peek().?) { 5905 .angle_bracket_right => { 5906 _ = m.next(); 5907 const lhs = try macroIntFromBool(c, node); 5908 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope)); 5909 node = try Tag.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5910 }, 5911 .angle_bracket_right_equal => { 5912 _ = m.next(); 5913 const lhs = try macroIntFromBool(c, node); 5914 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope)); 5915 node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5916 }, 5917 .angle_bracket_left => { 5918 _ = m.next(); 5919 const lhs = try macroIntFromBool(c, node); 5920 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope)); 5921 node = try Tag.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5922 }, 5923 .angle_bracket_left_equal => { 5924 _ = m.next(); 5925 const lhs = try macroIntFromBool(c, node); 5926 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope)); 5927 node = try Tag.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5928 }, 5929 else => return node, 5930 } 5931 } 5932 } 5933 5934 fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5935 var node = try parseCAddSubExpr(c, m, scope); 5936 while (true) { 5937 switch (m.peek().?) { 5938 .angle_bracket_angle_bracket_left => { 5939 _ = m.next(); 5940 const lhs = try macroIntFromBool(c, node); 5941 const rhs = try macroIntFromBool(c, try parseCAddSubExpr(c, m, scope)); 5942 node = try Tag.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5943 }, 5944 .angle_bracket_angle_bracket_right => { 5945 _ = m.next(); 5946 const lhs = try macroIntFromBool(c, node); 5947 const rhs = try macroIntFromBool(c, try parseCAddSubExpr(c, m, scope)); 5948 node = try Tag.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5949 }, 5950 else => return node, 5951 } 5952 } 5953 } 5954 5955 fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5956 var node = try parseCMulExpr(c, m, scope); 5957 while (true) { 5958 switch (m.peek().?) { 5959 .plus => { 5960 _ = m.next(); 5961 const lhs = try macroIntFromBool(c, node); 5962 const rhs = try macroIntFromBool(c, try parseCMulExpr(c, m, scope)); 5963 node = try Tag.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5964 }, 5965 .minus => { 5966 _ = m.next(); 5967 const lhs = try macroIntFromBool(c, node); 5968 const rhs = try macroIntFromBool(c, try parseCMulExpr(c, m, scope)); 5969 node = try Tag.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5970 }, 5971 else => return node, 5972 } 5973 } 5974 } 5975 5976 fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 5977 var node = try parseCCastExpr(c, m, scope); 5978 while (true) { 5979 switch (m.next().?) { 5980 .asterisk => { 5981 const lhs = try macroIntFromBool(c, node); 5982 const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope)); 5983 node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); 5984 }, 5985 .slash => { 5986 const lhs = try macroIntFromBool(c, node); 5987 const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope)); 5988 node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .div, .lhs = lhs, .rhs = rhs }); 5989 }, 5990 .percent => { 5991 const lhs = try macroIntFromBool(c, node); 5992 const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope)); 5993 node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .rem, .lhs = lhs, .rhs = rhs }); 5994 }, 5995 else => { 5996 m.i -= 1; 5997 return node; 5998 }, 5999 } 6000 } 6001 } 6002 6003 fn parseCCastExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 6004 switch (m.next().?) { 6005 .l_paren => { 6006 if (try parseCTypeName(c, m, scope, true)) |type_name| { 6007 while (true) { 6008 const next_token = m.next().?; 6009 switch (next_token) { 6010 .r_paren => break, 6011 else => |next_tag| { 6012 // Skip trailing blank defined before the RParen. 6013 if ((next_tag == .identifier or next_tag == .extended_identifier) and 6014 c.global_scope.blank_macros.contains(m.slice())) 6015 continue; 6016 6017 try m.fail( 6018 c, 6019 "unable to translate C expr: expected ')' instead got '{s}'", 6020 .{next_token.symbol()}, 6021 ); 6022 return error.ParseError; 6023 }, 6024 } 6025 } 6026 if (m.peek().? == .l_brace) { 6027 // initializer list 6028 return parseCPostfixExpr(c, m, scope, type_name); 6029 } 6030 const node_to_cast = try parseCCastExpr(c, m, scope); 6031 return Tag.helpers_cast.create(c.arena, .{ .lhs = type_name, .rhs = node_to_cast }); 6032 } 6033 }, 6034 else => {}, 6035 } 6036 m.i -= 1; 6037 return parseCUnaryExpr(c, m, scope); 6038 } 6039 6040 // allow_fail is set when unsure if we are parsing a type-name 6041 fn parseCTypeName(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) ParseError!?Node { 6042 if (try parseCSpecifierQualifierList(c, m, scope, allow_fail)) |node| { 6043 return try parseCAbstractDeclarator(c, m, node); 6044 } else { 6045 return null; 6046 } 6047 } 6048 6049 fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) ParseError!?Node { 6050 const tok = m.next().?; 6051 switch (tok) { 6052 .identifier, .extended_identifier => { 6053 if (c.global_scope.blank_macros.contains(m.slice())) { 6054 return try parseCSpecifierQualifierList(c, m, scope, allow_fail); 6055 } 6056 const mangled_name = scope.getAlias(m.slice()); 6057 if (!allow_fail or c.typedefs.contains(mangled_name)) { 6058 if (builtin_typedef_map.get(mangled_name)) |ty| return try Tag.type.create(c.arena, ty); 6059 return try Tag.identifier.create(c.arena, mangled_name); 6060 } 6061 }, 6062 .keyword_void => return try Tag.type.create(c.arena, "anyopaque"), 6063 .keyword_bool => return try Tag.type.create(c.arena, "bool"), 6064 .keyword_char, 6065 .keyword_int, 6066 .keyword_short, 6067 .keyword_long, 6068 .keyword_float, 6069 .keyword_double, 6070 .keyword_signed, 6071 .keyword_unsigned, 6072 .keyword_complex, 6073 => { 6074 m.i -= 1; 6075 return try parseCNumericType(c, m); 6076 }, 6077 .keyword_enum, .keyword_struct, .keyword_union => { 6078 // struct Foo will be declared as struct_Foo by transRecordDecl 6079 const slice = m.slice(); 6080 try m.skip(c, .identifier); 6081 6082 const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() }); 6083 return try Tag.identifier.create(c.arena, name); 6084 }, 6085 else => {}, 6086 } 6087 6088 if (allow_fail) { 6089 m.i -= 1; 6090 return null; 6091 } else { 6092 try m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{tok.symbol()}); 6093 return error.ParseError; 6094 } 6095 } 6096 6097 fn parseCNumericType(c: *Context, m: *MacroCtx) ParseError!Node { 6098 const KwCounter = struct { 6099 double: u8 = 0, 6100 long: u8 = 0, 6101 int: u8 = 0, 6102 float: u8 = 0, 6103 short: u8 = 0, 6104 char: u8 = 0, 6105 unsigned: u8 = 0, 6106 signed: u8 = 0, 6107 complex: u8 = 0, 6108 6109 fn eql(self: @This(), other: @This()) bool { 6110 return meta.eql(self, other); 6111 } 6112 }; 6113 6114 // Yes, these can be in *any* order 6115 // This still doesn't cover cases where for example volatile is intermixed 6116 6117 var kw = KwCounter{}; 6118 // prevent overflow 6119 var i: u8 = 0; 6120 while (i < math.maxInt(u8)) : (i += 1) { 6121 switch (m.next().?) { 6122 .keyword_double => kw.double += 1, 6123 .keyword_long => kw.long += 1, 6124 .keyword_int => kw.int += 1, 6125 .keyword_float => kw.float += 1, 6126 .keyword_short => kw.short += 1, 6127 .keyword_char => kw.char += 1, 6128 .keyword_unsigned => kw.unsigned += 1, 6129 .keyword_signed => kw.signed += 1, 6130 .keyword_complex => kw.complex += 1, 6131 else => { 6132 m.i -= 1; 6133 break; 6134 }, 6135 } 6136 } 6137 6138 if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 })) 6139 return Tag.type.create(c.arena, "c_int"); 6140 6141 if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 })) 6142 return Tag.type.create(c.arena, "c_uint"); 6143 6144 if (kw.eql(.{ .long = 1 }) or kw.eql(.{ .signed = 1, .long = 1 }) or kw.eql(.{ .long = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 1, .int = 1 })) 6145 return Tag.type.create(c.arena, "c_long"); 6146 6147 if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 })) 6148 return Tag.type.create(c.arena, "c_ulong"); 6149 6150 if (kw.eql(.{ .long = 2 }) or kw.eql(.{ .signed = 1, .long = 2 }) or kw.eql(.{ .long = 2, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 2, .int = 1 })) 6151 return Tag.type.create(c.arena, "c_longlong"); 6152 6153 if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 })) 6154 return Tag.type.create(c.arena, "c_ulonglong"); 6155 6156 if (kw.eql(.{ .signed = 1, .char = 1 })) 6157 return Tag.type.create(c.arena, "i8"); 6158 6159 if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 })) 6160 return Tag.type.create(c.arena, "u8"); 6161 6162 if (kw.eql(.{ .short = 1 }) or kw.eql(.{ .signed = 1, .short = 1 }) or kw.eql(.{ .short = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .short = 1, .int = 1 })) 6163 return Tag.type.create(c.arena, "c_short"); 6164 6165 if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 })) 6166 return Tag.type.create(c.arena, "c_ushort"); 6167 6168 if (kw.eql(.{ .float = 1 })) 6169 return Tag.type.create(c.arena, "f32"); 6170 6171 if (kw.eql(.{ .double = 1 })) 6172 return Tag.type.create(c.arena, "f64"); 6173 6174 if (kw.eql(.{ .long = 1, .double = 1 })) { 6175 try m.fail(c, "unable to translate: TODO long double", .{}); 6176 return error.ParseError; 6177 } 6178 6179 if (kw.eql(.{ .float = 1, .complex = 1 })) { 6180 try m.fail(c, "unable to translate: TODO _Complex", .{}); 6181 return error.ParseError; 6182 } 6183 6184 if (kw.eql(.{ .double = 1, .complex = 1 })) { 6185 try m.fail(c, "unable to translate: TODO _Complex", .{}); 6186 return error.ParseError; 6187 } 6188 6189 if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) { 6190 try m.fail(c, "unable to translate: TODO _Complex", .{}); 6191 return error.ParseError; 6192 } 6193 6194 try m.fail(c, "unable to translate: invalid numeric type", .{}); 6195 return error.ParseError; 6196 } 6197 6198 fn parseCAbstractDeclarator(c: *Context, m: *MacroCtx, node: Node) ParseError!Node { 6199 switch (m.next().?) { 6200 .asterisk => { 6201 // last token of `node` 6202 const prev_id = m.list[m.i - 1].id; 6203 6204 if (prev_id == .keyword_void) { 6205 const ptr = try Tag.single_pointer.create(c.arena, .{ 6206 .is_const = false, 6207 .is_volatile = false, 6208 .elem_type = node, 6209 }); 6210 return Tag.optional_type.create(c.arena, ptr); 6211 } else { 6212 return Tag.c_pointer.create(c.arena, .{ 6213 .is_const = false, 6214 .is_volatile = false, 6215 .elem_type = node, 6216 }); 6217 } 6218 }, 6219 else => { 6220 m.i -= 1; 6221 return node; 6222 }, 6223 } 6224 } 6225 6226 fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node) ParseError!Node { 6227 var node = try parseCPostfixExprInner(c, m, scope, type_name); 6228 // In C the preprocessor would handle concatting strings while expanding macros. 6229 // This should do approximately the same by concatting any strings and identifiers 6230 // after a primary or postfix expression. 6231 while (true) { 6232 switch (m.peek().?) { 6233 .string_literal, 6234 .string_literal_utf_16, 6235 .string_literal_utf_8, 6236 .string_literal_utf_32, 6237 .string_literal_wide, 6238 => {}, 6239 .identifier, .extended_identifier => { 6240 const tok = m.list[m.i + 1]; 6241 const slice = m.source[tok.start..tok.end]; 6242 if (c.global_scope.blank_macros.contains(slice)) { 6243 m.i += 1; 6244 continue; 6245 } 6246 }, 6247 else => break, 6248 } 6249 const rhs = try parseCPostfixExprInner(c, m, scope, type_name); 6250 node = try Tag.array_cat.create(c.arena, .{ .lhs = node, .rhs = rhs }); 6251 } 6252 return node; 6253 } 6254 6255 fn parseCPostfixExprInner(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node) ParseError!Node { 6256 var node = type_name orelse try parseCPrimaryExpr(c, m, scope); 6257 while (true) { 6258 switch (m.next().?) { 6259 .period => { 6260 try m.skip(c, .identifier); 6261 6262 node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() }); 6263 }, 6264 .arrow => { 6265 try m.skip(c, .identifier); 6266 6267 const deref = try Tag.deref.create(c.arena, node); 6268 node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() }); 6269 }, 6270 .l_bracket => { 6271 const index_val = try macroIntFromBool(c, try parseCExpr(c, m, scope)); 6272 const index = try Tag.as.create(c.arena, .{ 6273 .lhs = try Tag.type.create(c.arena, "usize"), 6274 .rhs = try Tag.int_cast.create(c.arena, index_val), 6275 }); 6276 node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index }); 6277 try m.skip(c, .r_bracket); 6278 }, 6279 .l_paren => { 6280 if (m.peek().? == .r_paren) { 6281 m.i += 1; 6282 node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &[0]Node{} }); 6283 } else { 6284 var args = std.ArrayList(Node).init(c.gpa); 6285 defer args.deinit(); 6286 while (true) { 6287 const arg = try parseCCondExpr(c, m, scope); 6288 try args.append(arg); 6289 const next_id = m.next().?; 6290 switch (next_id) { 6291 .comma => {}, 6292 .r_paren => break, 6293 else => { 6294 try m.fail(c, "unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()}); 6295 return error.ParseError; 6296 }, 6297 } 6298 } 6299 node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) }); 6300 } 6301 }, 6302 .l_brace => { 6303 // Check for designated field initializers 6304 if (m.peek().? == .period) { 6305 var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(c.gpa); 6306 defer init_vals.deinit(); 6307 6308 while (true) { 6309 try m.skip(c, .period); 6310 try m.skip(c, .identifier); 6311 const name = m.slice(); 6312 try m.skip(c, .equal); 6313 6314 const val = try parseCCondExpr(c, m, scope); 6315 try init_vals.append(.{ .name = name, .value = val }); 6316 const next_id = m.next().?; 6317 switch (next_id) { 6318 .comma => {}, 6319 .r_brace => break, 6320 else => { 6321 try m.fail(c, "unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()}); 6322 return error.ParseError; 6323 }, 6324 } 6325 } 6326 const tuple_node = try Tag.container_init_dot.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items)); 6327 node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node }); 6328 continue; 6329 } 6330 6331 var init_vals = std.ArrayList(Node).init(c.gpa); 6332 defer init_vals.deinit(); 6333 6334 while (true) { 6335 const val = try parseCCondExpr(c, m, scope); 6336 try init_vals.append(val); 6337 const next_id = m.next().?; 6338 switch (next_id) { 6339 .comma => {}, 6340 .r_brace => break, 6341 else => { 6342 try m.fail(c, "unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()}); 6343 return error.ParseError; 6344 }, 6345 } 6346 } 6347 const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items)); 6348 node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node }); 6349 }, 6350 .plus_plus, .minus_minus => { 6351 try m.fail(c, "TODO postfix inc/dec expr", .{}); 6352 return error.ParseError; 6353 }, 6354 else => { 6355 m.i -= 1; 6356 return node; 6357 }, 6358 } 6359 } 6360 } 6361 6362 fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { 6363 switch (m.next().?) { 6364 .bang => { 6365 const operand = try macroIntToBool(c, try parseCCastExpr(c, m, scope)); 6366 return Tag.not.create(c.arena, operand); 6367 }, 6368 .minus => { 6369 const operand = try macroIntFromBool(c, try parseCCastExpr(c, m, scope)); 6370 return Tag.negate.create(c.arena, operand); 6371 }, 6372 .plus => return try parseCCastExpr(c, m, scope), 6373 .tilde => { 6374 const operand = try macroIntFromBool(c, try parseCCastExpr(c, m, scope)); 6375 return Tag.bit_not.create(c.arena, operand); 6376 }, 6377 .asterisk => { 6378 const operand = try parseCCastExpr(c, m, scope); 6379 return Tag.deref.create(c.arena, operand); 6380 }, 6381 .ampersand => { 6382 const operand = try parseCCastExpr(c, m, scope); 6383 return Tag.address_of.create(c.arena, operand); 6384 }, 6385 .keyword_sizeof => { 6386 const operand = if (m.peek().? == .l_paren) blk: { 6387 _ = m.next(); 6388 const inner = (try parseCTypeName(c, m, scope, false)).?; 6389 try m.skip(c, .r_paren); 6390 break :blk inner; 6391 } else try parseCUnaryExpr(c, m, scope); 6392 6393 return Tag.helpers_sizeof.create(c.arena, operand); 6394 }, 6395 .keyword_alignof => { 6396 // TODO this won't work if using <stdalign.h>'s 6397 // #define alignof _Alignof 6398 try m.skip(c, .l_paren); 6399 const operand = (try parseCTypeName(c, m, scope, false)).?; 6400 try m.skip(c, .r_paren); 6401 6402 return Tag.alignof.create(c.arena, operand); 6403 }, 6404 .plus_plus, .minus_minus => { 6405 try m.fail(c, "TODO unary inc/dec expr", .{}); 6406 return error.ParseError; 6407 }, 6408 else => { 6409 m.i -= 1; 6410 return try parseCPostfixExpr(c, m, scope, null); 6411 }, 6412 } 6413 } 6414 6415 fn getContainer(c: *Context, node: Node) ?Node { 6416 switch (node.tag()) { 6417 .@"union", 6418 .@"struct", 6419 .address_of, 6420 .bit_not, 6421 .not, 6422 .optional_type, 6423 .negate, 6424 .negate_wrap, 6425 .array_type, 6426 .c_pointer, 6427 .single_pointer, 6428 => return node, 6429 6430 .identifier => { 6431 const ident = node.castTag(.identifier).?; 6432 if (c.global_scope.sym_table.get(ident.data)) |value| { 6433 if (value.castTag(.var_decl)) |var_decl| 6434 return getContainer(c, var_decl.data.init.?); 6435 if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl| 6436 return getContainer(c, var_decl.data.init); 6437 } 6438 }, 6439 6440 .field_access => { 6441 const field_access = node.castTag(.field_access).?; 6442 6443 if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| { 6444 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { 6445 for (container.data.fields) |field| { 6446 if (mem.eql(u8, field.name, field_access.data.field_name)) { 6447 return getContainer(c, field.type); 6448 } 6449 } 6450 } 6451 } 6452 }, 6453 6454 else => {}, 6455 } 6456 return null; 6457 } 6458 6459 fn getContainerTypeOf(c: *Context, ref: Node) ?Node { 6460 if (ref.castTag(.identifier)) |ident| { 6461 if (c.global_scope.sym_table.get(ident.data)) |value| { 6462 if (value.castTag(.var_decl)) |var_decl| { 6463 return getContainer(c, var_decl.data.type); 6464 } 6465 } 6466 } else if (ref.castTag(.field_access)) |field_access| { 6467 if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| { 6468 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { 6469 for (container.data.fields) |field| { 6470 if (mem.eql(u8, field.name, field_access.data.field_name)) { 6471 return getContainer(c, field.type); 6472 } 6473 } 6474 } else return ty_node; 6475 } 6476 } 6477 return null; 6478 } 6479 6480 fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func { 6481 const init = if (ref.castTag(.var_decl)) |v| 6482 v.data.init orelse return null 6483 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| 6484 v.data.init 6485 else 6486 return null; 6487 if (getContainerTypeOf(c, init)) |ty_node| { 6488 if (ty_node.castTag(.optional_type)) |prefix| { 6489 if (prefix.data.castTag(.single_pointer)) |sp| { 6490 if (sp.data.elem_type.castTag(.func)) |fn_proto| { 6491 return fn_proto; 6492 } 6493 } 6494 } 6495 } 6496 return null; 6497 } 6498 6499 fn addMacros(c: *Context) !void { 6500 var it = c.global_scope.macro_table.iterator(); 6501 while (it.next()) |entry| { 6502 if (getFnProto(c, entry.value_ptr.*)) |proto_node| { 6503 // If a macro aliases a global variable which is a function pointer, we conclude that 6504 // the macro is intended to represent a function that assumes the function pointer 6505 // variable is non-null and calls it. 6506 try addTopLevelDecl(c, entry.key_ptr.*, try transCreateNodeMacroFn(c, entry.key_ptr.*, entry.value_ptr.*, proto_node)); 6507 } else { 6508 try addTopLevelDecl(c, entry.key_ptr.*, entry.value_ptr.*); 6509 } 6510 } 6511 }