| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| | 4 | const CallingConvention = std.builtin.CallingConvention; |
| 4 | const translate_c = @import("translate_c.zig"); | 5 | const translate_c = @import("translate_c.zig"); |
| 5 | const aro = @import("aro"); | 6 | const aro = @import("aro"); |
| 6 | const Tree = aro.Tree; | 7 | const Tree = aro.Tree; |
| ... | @@ -99,6 +100,12 @@ fn failDecl(c: *Context, loc: TokenIndex, name: []const u8, comptime format: []c | ... | @@ -99,6 +100,12 @@ fn failDecl(c: *Context, loc: TokenIndex, name: []const u8, comptime format: []c |
| 99 | try c.global_scope.nodes.append(try ZigTag.warning.create(c.arena, location_comment)); | 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 | pub fn translate( | 109 | pub fn translate( |
| 103 | gpa: mem.Allocator, | 110 | gpa: mem.Allocator, |
| 104 | comp: *Compilation, | 111 | comp: *Compilation, |
| ... | @@ -299,12 +306,112 @@ fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void { | ... | @@ -299,12 +306,112 @@ fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void { |
| 299 | fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void { | 306 | fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void { |
| 300 | @panic("TODO"); | 307 | @panic("TODO"); |
| 301 | } | 308 | } |
| 302 | fn transFnDecl(_: *Context, _: NodeIndex) Error!void { | 309 | |
| 303 | @panic("TODO"); | 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 | fn transVarDecl(_: *Context, _: NodeIndex, _: ?usize) Error!void { | 411 | fn transVarDecl(_: *Context, _: NodeIndex, _: ?usize) Error!void { |
| 306 | @panic("TODO"); | 412 | @panic("TODO"); |
| 307 | } | 413 | } |
| | 414 | |
| 308 | fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: []const NodeIndex) Error!void { | 415 | fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: []const NodeIndex) Error!void { |
| 309 | const node_types = c.tree.nodes.items(.ty); | 416 | const node_types = c.tree.nodes.items(.ty); |
| 310 | const ty = node_types[@intFromEnum(enum_decl)]; | 417 | const ty = node_types[@intFromEnum(enum_decl)]; |
| ... | @@ -393,8 +500,6 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: | ... | @@ -393,8 +500,6 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: |
| 393 | } | 500 | } |
| 394 | | 501 | |
| 395 | fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode { | 502 | fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode { |
| 396 | _ = source_loc; | | |
| 397 | _ = scope; | | |
| 398 | const ty = raw_ty.canonicalize(.standard); | 503 | const ty = raw_ty.canonicalize(.standard); |
| 399 | switch (ty.specifier) { | 504 | switch (ty.specifier) { |
| 400 | .void => return ZigTag.type.create(c.arena, "anyopaque"), | 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,10 +523,112 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) T |
| 418 | .long_double => return ZigTag.type.create(c.arena, "c_longdouble"), | 523 | .long_double => return ZigTag.type.create(c.arena, "c_longdouble"), |
| 419 | .float80 => return ZigTag.type.create(c.arena, "f80"), | 524 | .float80 => return ZigTag.type.create(c.arena, "f80"), |
| 420 | .float128 => return ZigTag.type.create(c.arena, "f128"), | 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 | fn transStmt(c: *Context, node: NodeIndex) TransError!void { | 632 | fn transStmt(c: *Context, node: NodeIndex) TransError!void { |
| 426 | _ = try c.transExpr(node, .unused); | 633 | _ = try c.transExpr(node, .unused); |
| 427 | } | 634 | } |