| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | const CallingConvention = std.builtin.CallingConvention; |
| 4 | 5 | const translate_c = @import("translate_c.zig"); |
| 5 | 6 | const aro = @import("aro"); |
| 6 | 7 | const Tree = aro.Tree; |
| ... | ... | @@ -99,6 +100,12 @@ fn failDecl(c: *Context, loc: TokenIndex, name: []const u8, comptime format: []c |
| 99 | 100 | try c.global_scope.nodes.append(try ZigTag.warning.create(c.arena, location_comment)); |
| 100 | 101 | } |
| 101 | 102 | |
| 103 | fn warn(c: *Context, scope: *Scope, loc: TokenIndex, comptime format: []const u8, args: anytype) !void { |
| 104 | const str = try c.locStr(loc); |
| 105 | const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, .{str} ++ args); |
| 106 | try scope.appendNode(try ZigTag.warning.create(c.arena, value)); |
| 107 | } |
| 108 | |
| 102 | 109 | pub fn translate( |
| 103 | 110 | gpa: mem.Allocator, |
| 104 | 111 | comp: *Compilation, |
| ... | ... | @@ -299,12 +306,112 @@ fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void { |
| 299 | 306 | fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void { |
| 300 | 307 | @panic("TODO"); |
| 301 | 308 | } |
| 302 | | fn transFnDecl(_: *Context, _: NodeIndex) Error!void { |
| 303 | | @panic("TODO"); |
| 309 | |
| 310 | fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void { |
| 311 | const raw_ty = c.tree.nodes.items(.ty)[@intFromEnum(fn_decl)]; |
| 312 | const fn_ty = raw_ty.canonicalize(.standard); |
| 313 | const node_data = c.tree.nodes.items(.data)[@intFromEnum(fn_decl)]; |
| 314 | if (c.decl_table.get(@intFromPtr(fn_ty.data.func))) |_| |
| 315 | return; // Avoid processing this decl twice |
| 316 | |
| 317 | const fn_name = c.tree.tokSlice(node_data.decl.name); |
| 318 | if (c.global_scope.sym_table.contains(fn_name)) |
| 319 | return; // Avoid processing this decl twice |
| 320 | |
| 321 | const fn_decl_loc = 0; // TODO |
| 322 | const has_body = node_data.decl.node != .none; |
| 323 | const is_always_inline = has_body and raw_ty.getAttribute(.always_inline) != null; |
| 324 | const proto_ctx = FnProtoContext{ |
| 325 | .fn_name = fn_name, |
| 326 | .is_inline = is_always_inline, |
| 327 | .is_extern = !has_body, |
| 328 | .is_export = switch (c.tree.nodes.items(.tag)[@intFromEnum(fn_decl)]) { |
| 329 | .fn_proto, |
| 330 | .fn_def => has_body and !is_always_inline, |
| 331 | |
| 332 | .inline_fn_proto, |
| 333 | .inline_fn_def, |
| 334 | .inline_static_fn_proto, |
| 335 | .inline_static_fn_def, |
| 336 | .static_fn_proto, |
| 337 | .static_fn_def => false, |
| 338 | |
| 339 | else => unreachable, |
| 340 | }, |
| 341 | }; |
| 342 | |
| 343 | const proto_node = transFnType(c, &c.global_scope.base, raw_ty, fn_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) { |
| 344 | error.UnsupportedType => { |
| 345 | return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); |
| 346 | }, |
| 347 | error.OutOfMemory => |e| return e, |
| 348 | }; |
| 349 | |
| 350 | if (!has_body) { |
| 351 | return addTopLevelDecl(c, fn_name, proto_node); |
| 352 | } |
| 353 | const proto_payload = proto_node.castTag(.func).?; |
| 354 | |
| 355 | // actual function definition with body |
| 356 | const body_stmt = node_data.decl.node; |
| 357 | _ = body_stmt; |
| 358 | var block_scope = try Scope.Block.init(c, &c.global_scope.base, false); |
| 359 | block_scope.return_type = fn_ty.data.func.return_type; |
| 360 | defer block_scope.deinit(); |
| 361 | |
| 362 | var scope = &block_scope.base; |
| 363 | _ = scope; |
| 364 | |
| 365 | var param_id: c_uint = 0; |
| 366 | for (proto_payload.data.params, fn_ty.data.func.params) |*param, param_info| { |
| 367 | const param_name = param.name orelse { |
| 368 | proto_payload.data.is_extern = true; |
| 369 | proto_payload.data.is_export = false; |
| 370 | proto_payload.data.is_inline = false; |
| 371 | try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name}); |
| 372 | return addTopLevelDecl(c, fn_name, proto_node); |
| 373 | }; |
| 374 | |
| 375 | const is_const = param_info.ty.qual.@"const"; |
| 376 | |
| 377 | const mangled_param_name = try block_scope.makeMangledName(c, param_name); |
| 378 | param.name = mangled_param_name; |
| 379 | |
| 380 | if (!is_const) { |
| 381 | const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); |
| 382 | const arg_name = try block_scope.makeMangledName(c, bare_arg_name); |
| 383 | param.name = arg_name; |
| 384 | |
| 385 | const redecl_node = try ZigTag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); |
| 386 | try block_scope.statements.append(redecl_node); |
| 387 | } |
| 388 | try block_scope.discardVariable(c, mangled_param_name); |
| 389 | |
| 390 | param_id += 1; |
| 391 | } |
| 392 | |
| 393 | // const casted_body = @as(*const clang.CompoundStmt, @ptrCast(body_stmt)); |
| 394 | // transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) { |
| 395 | // error.OutOfMemory => |e| return e, |
| 396 | // error.UnsupportedTranslation, |
| 397 | // error.UnsupportedType, |
| 398 | // => { |
| 399 | // proto_payload.data.is_extern = true; |
| 400 | // proto_payload.data.is_export = false; |
| 401 | // proto_payload.data.is_inline = false; |
| 402 | // try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{}); |
| 403 | // return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 404 | // }, |
| 405 | // }; |
| 406 | |
| 407 | proto_payload.data.body = try block_scope.complete(c); |
| 408 | return addTopLevelDecl(c, fn_name, proto_node); |
| 304 | 409 | } |
| 410 | |
| 305 | 411 | fn transVarDecl(_: *Context, _: NodeIndex, _: ?usize) Error!void { |
| 306 | 412 | @panic("TODO"); |
| 307 | 413 | } |
| 414 | |
| 308 | 415 | fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: []const NodeIndex) Error!void { |
| 309 | 416 | const node_types = c.tree.nodes.items(.ty); |
| 310 | 417 | const ty = node_types[@intFromEnum(enum_decl)]; |
| ... | ... | @@ -393,8 +500,6 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: |
| 393 | 500 | } |
| 394 | 501 | |
| 395 | 502 | fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode { |
| 396 | | _ = source_loc; |
| 397 | | _ = scope; |
| 398 | 503 | const ty = raw_ty.canonicalize(.standard); |
| 399 | 504 | switch (ty.specifier) { |
| 400 | 505 | .void => return ZigTag.type.create(c.arena, "anyopaque"), |
| ... | ... | @@ -418,10 +523,112 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) T |
| 418 | 523 | .long_double => return ZigTag.type.create(c.arena, "c_longdouble"), |
| 419 | 524 | .float80 => return ZigTag.type.create(c.arena, "f80"), |
| 420 | 525 | .float128 => return ZigTag.type.create(c.arena, "f128"), |
| 421 | | else => @panic("TODO"), |
| 526 | .func, |
| 527 | .var_args_func, |
| 528 | .old_style_func, |
| 529 | => return transFnType(c, scope, raw_ty, ty, source_loc, .{}), |
| 530 | else => return error.UnsupportedType, |
| 422 | 531 | } |
| 423 | 532 | } |
| 424 | 533 | |
| 534 | fn zigAlignment(bit_alignment: u29) u32 { |
| 535 | return bit_alignment / 8; |
| 536 | } |
| 537 | |
| 538 | const FnProtoContext = struct { |
| 539 | is_pub: bool = false, |
| 540 | is_export: bool = false, |
| 541 | is_extern: bool = false, |
| 542 | is_inline: bool = false, |
| 543 | fn_name: ?[]const u8 = null, |
| 544 | }; |
| 545 | |
| 546 | fn transFnType( |
| 547 | c: *Context, |
| 548 | scope: *Scope, |
| 549 | raw_ty: Type, |
| 550 | fn_ty: Type, |
| 551 | source_loc: TokenIndex, |
| 552 | ctx: FnProtoContext, |
| 553 | ) !ZigNode { |
| 554 | const param_count: usize = fn_ty.data.func.params.len; |
| 555 | var fn_params = try c.arena.alloc(ast.Payload.Param, param_count); |
| 556 | |
| 557 | for (fn_ty.data.func.params, fn_params) |param_info, *param_node| { |
| 558 | const param_ty = param_info.ty; |
| 559 | const is_noalias = param_ty.qual.restrict; |
| 560 | |
| 561 | const param_name: ?[]const u8 = if (param_info.name == .empty) |
| 562 | null |
| 563 | else |
| 564 | c.mapper.lookup(param_info.name); |
| 565 | |
| 566 | const type_node = try transType(c, scope, param_ty, param_info.name_tok); |
| 567 | param_node.* = .{ |
| 568 | .is_noalias = is_noalias, |
| 569 | .name = param_name, |
| 570 | .type = type_node, |
| 571 | }; |
| 572 | } |
| 573 | |
| 574 | const linksection_string = blk: { |
| 575 | if (raw_ty.getAttribute(.section)) |section| { |
| 576 | break :blk section.name.slice(c.tree.strings); |
| 577 | } |
| 578 | break :blk null; |
| 579 | }; |
| 580 | |
| 581 | const alignment = if (raw_ty.requestedAlignment(c.comp)) |alignment| zigAlignment(alignment) else null; |
| 582 | |
| 583 | const explicit_callconv = null; |
| 584 | // const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc; |
| 585 | |
| 586 | const return_type_node = blk: { |
| 587 | if (raw_ty.getAttribute(.noreturn) != null) { |
| 588 | break :blk ZigTag.noreturn_type.init(); |
| 589 | } else { |
| 590 | const return_ty = fn_ty.data.func.return_type; |
| 591 | if (return_ty.is(.void)) { |
| 592 | // convert primitive anyopaque to actual void (only for return type) |
| 593 | break :blk ZigTag.void_type.init(); |
| 594 | } else { |
| 595 | break :blk transType(c, scope, return_ty, source_loc) catch |err| switch (err) { |
| 596 | error.UnsupportedType => { |
| 597 | try warn(c, scope, source_loc, "unsupported function proto return type", .{}); |
| 598 | return err; |
| 599 | }, |
| 600 | error.OutOfMemory => |e| return e, |
| 601 | }; |
| 602 | } |
| 603 | } |
| 604 | }; |
| 605 | |
| 606 | const payload = try c.arena.create(ast.Payload.Func); |
| 607 | payload.* = .{ |
| 608 | .base = .{ .tag = .func }, |
| 609 | .data = .{ |
| 610 | .is_pub = ctx.is_pub, |
| 611 | .is_extern = ctx.is_extern, |
| 612 | .is_export = ctx.is_export, |
| 613 | .is_inline = ctx.is_inline, |
| 614 | .is_var_args = switch (fn_ty.specifier) { |
| 615 | .func => false, |
| 616 | .var_args_func => true, |
| 617 | .old_style_func => !ctx.is_export and !ctx.is_inline, |
| 618 | else => unreachable, |
| 619 | }, |
| 620 | .name = ctx.fn_name, |
| 621 | .linksection_string = linksection_string, |
| 622 | .explicit_callconv = explicit_callconv, |
| 623 | .params = fn_params, |
| 624 | .return_type = return_type_node, |
| 625 | .body = null, |
| 626 | .alignment = alignment, |
| 627 | }, |
| 628 | }; |
| 629 | return ZigNode.initPayload(&payload.base); |
| 630 | } |
| 631 | |
| 425 | 632 | fn transStmt(c: *Context, node: NodeIndex) TransError!void { |
| 426 | 633 | _ = try c.transExpr(node, .unused); |
| 427 | 634 | } |