| ... | ... | @@ -719,6 +719,44 @@ fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, |
| 719 | 719 | transQualType(c, scope, qt, loc); |
| 720 | 720 | } |
| 721 | 721 | |
| 722 | /// This is used in global scope to convert a string literal `S` to [*c]u8: |
| 723 | /// &(struct { |
| 724 | /// var static: @TypeOf(S.*) = S.*; |
| 725 | /// }).static; |
| 726 | fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node { |
| 727 | const var_name = Scope.Block.StaticInnerName; |
| 728 | |
| 729 | const derefed = try Tag.deref.create(c.arena, str); |
| 730 | const var_type = try Tag.typeof.create(c.arena, derefed); |
| 731 | |
| 732 | const variables = try c.arena.alloc(Node, 1); |
| 733 | variables[0] = try Tag.var_decl.create(c.arena, .{ |
| 734 | .is_pub = false, |
| 735 | .is_const = false, |
| 736 | .is_extern = false, |
| 737 | .is_export = false, |
| 738 | .is_threadlocal = false, |
| 739 | .linksection_string = null, |
| 740 | .alignment = null, |
| 741 | .name = var_name, |
| 742 | .type = var_type, |
| 743 | .init = derefed, |
| 744 | }); |
| 745 | |
| 746 | const anon_struct = try Tag.@"struct".create(c.arena, .{ |
| 747 | .layout = .none, |
| 748 | .fields = &.{}, |
| 749 | .functions = &.{}, |
| 750 | .variables = variables, |
| 751 | }); |
| 752 | |
| 753 | const member_access = try Tag.field_access.create(c.arena, .{ |
| 754 | .lhs = anon_struct, |
| 755 | .field_name = var_name, |
| 756 | }); |
| 757 | return Tag.address_of.create(c.arena, member_access); |
| 758 | } |
| 759 | |
| 722 | 760 | /// if mangled_name is not null, this var decl was declared in a block scope. |
| 723 | 761 | fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void { |
| 724 | 762 | const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); |
| ... | ... | @@ -779,6 +817,8 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 779 | 817 | }; |
| 780 | 818 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) { |
| 781 | 819 | init_node = try Tag.bool_to_int.create(c.arena, init_node.?); |
| 820 | } else if (init_node.?.tag() == .string_literal and qualTypeIsCharStar(qual_type)) { |
| 821 | init_node = try stringLiteralToCharStar(c, init_node.?); |
| 782 | 822 | } |
| 783 | 823 | } else { |
| 784 | 824 | init_node = Tag.undefined_literal.init(); |
| ... | ... | @@ -1101,9 +1141,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1101 | 1141 | record_payload.* = .{ |
| 1102 | 1142 | .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, |
| 1103 | 1143 | .data = .{ |
| 1104 | | .is_packed = is_packed, |
| 1144 | .layout = if (is_packed) .@"packed" else .@"extern", |
| 1105 | 1145 | .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), |
| 1106 | 1146 | .functions = try c.arena.dupe(Node, functions.items), |
| 1147 | .variables = &.{}, |
| 1107 | 1148 | }, |
| 1108 | 1149 | }; |
| 1109 | 1150 | break :blk Node.initPayload(&record_payload.base); |
| ... | ... | @@ -1805,6 +1846,9 @@ fn transDeclStmtOne( |
| 1805 | 1846 | Tag.undefined_literal.init(); |
| 1806 | 1847 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { |
| 1807 | 1848 | init_node = try Tag.bool_to_int.create(c.arena, init_node); |
| 1849 | } else if (init_node.tag() == .string_literal and qualTypeIsCharStar(qual_type)) { |
| 1850 | const dst_type_node = try transQualType(c, scope, qual_type, loc); |
| 1851 | init_node = try removeCVQualifiers(c, dst_type_node, init_node); |
| 1808 | 1852 | } |
| 1809 | 1853 | |
| 1810 | 1854 | const var_name: []const u8 = if (is_static_local) Scope.Block.StaticInnerName else mangled_name; |
| ... | ... | @@ -2522,9 +2566,19 @@ fn transInitListExprRecord( |
| 2522 | 2566 | raw_name = try mem.dupe(c.arena, u8, name); |
| 2523 | 2567 | } |
| 2524 | 2568 | |
| 2569 | var init_expr = try transExpr(c, scope, elem_expr, .used); |
| 2570 | const field_qt = field_decl.getType(); |
| 2571 | if (init_expr.tag() == .string_literal and qualTypeIsCharStar(field_qt)) { |
| 2572 | if (scope.id == .root) { |
| 2573 | init_expr = try stringLiteralToCharStar(c, init_expr); |
| 2574 | } else { |
| 2575 | const dst_type_node = try transQualType(c, scope, field_qt, loc); |
| 2576 | init_expr = try removeCVQualifiers(c, dst_type_node, init_expr); |
| 2577 | } |
| 2578 | } |
| 2525 | 2579 | try field_inits.append(.{ |
| 2526 | 2580 | .name = raw_name, |
| 2527 | | .value = try transExpr(c, scope, elem_expr, .used), |
| 2581 | .value = init_expr, |
| 2528 | 2582 | }); |
| 2529 | 2583 | } |
| 2530 | 2584 | if (ty_node.castTag(.identifier)) |ident_node| { |
| ... | ... | @@ -3459,6 +3513,10 @@ fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result |
| 3459 | 3513 | const param_qt = fn_proto.getParamType(@intCast(c_uint, i)); |
| 3460 | 3514 | if (isBoolRes(arg) and cIsNativeInt(param_qt)) { |
| 3461 | 3515 | arg = try Tag.bool_to_int.create(c.arena, arg); |
| 3516 | } else if (arg.tag() == .string_literal and qualTypeIsCharStar(param_qt)) { |
| 3517 | const loc = @ptrCast(*const clang.Stmt, stmt).getBeginLoc(); |
| 3518 | const dst_type_node = try transQualType(c, scope, param_qt, loc); |
| 3519 | arg = try removeCVQualifiers(c, dst_type_node, arg); |
| 3462 | 3520 | } |
| 3463 | 3521 | } |
| 3464 | 3522 | }, |
| ... | ... | @@ -3835,6 +3893,12 @@ fn transCreateCompoundAssign( |
| 3835 | 3893 | return block_scope.complete(c); |
| 3836 | 3894 | } |
| 3837 | 3895 | |
| 3896 | // Casting away const or volatile requires us to use @intToPtr |
| 3897 | fn removeCVQualifiers(c: *Context, dst_type_node: Node, expr: Node) Error!Node { |
| 3898 | const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); |
| 3899 | return Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int }); |
| 3900 | } |
| 3901 | |
| 3838 | 3902 | fn transCPtrCast( |
| 3839 | 3903 | c: *Context, |
| 3840 | 3904 | scope: *Scope, |
| ... | ... | @@ -3854,10 +3918,7 @@ fn transCPtrCast( |
| 3854 | 3918 | (src_child_type.isVolatileQualified() and |
| 3855 | 3919 | !child_type.isVolatileQualified()))) |
| 3856 | 3920 | { |
| 3857 | | // Casting away const or volatile requires us to use @intToPtr |
| 3858 | | const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); |
| 3859 | | const int_to_ptr = try Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int }); |
| 3860 | | return int_to_ptr; |
| 3921 | return removeCVQualifiers(c, dst_type_node, expr); |
| 3861 | 3922 | } else { |
| 3862 | 3923 | // Implicit downcasting from higher to lower alignment values is forbidden, |
| 3863 | 3924 | // use @alignCast to side-step this problem |
| ... | ... | @@ -4217,6 +4278,26 @@ fn typeIsOpaque(c: *Context, ty: *const clang.Type, loc: clang.SourceLocation) b |
| 4217 | 4278 | } |
| 4218 | 4279 | } |
| 4219 | 4280 | |
| 4281 | /// plain `char *` (not const; not explicitly signed or unsigned) |
| 4282 | fn qualTypeIsCharStar(qt: clang.QualType) bool { |
| 4283 | if (qualTypeIsPtr(qt)) { |
| 4284 | const child_qt = qualTypeCanon(qt).getPointeeType(); |
| 4285 | return cIsUnqualifiedChar(child_qt) and !child_qt.isConstQualified(); |
| 4286 | } |
| 4287 | return false; |
| 4288 | } |
| 4289 | |
| 4290 | /// C `char` without explicit signed or unsigned qualifier |
| 4291 | fn cIsUnqualifiedChar(qt: clang.QualType) bool { |
| 4292 | const c_type = qualTypeCanon(qt); |
| 4293 | if (c_type.getTypeClass() != .Builtin) return false; |
| 4294 | const builtin_ty = @ptrCast(*const clang.BuiltinType, c_type); |
| 4295 | return switch (builtin_ty.getKind()) { |
| 4296 | .Char_S, .Char_U => true, |
| 4297 | else => false, |
| 4298 | }; |
| 4299 | } |
| 4300 | |
| 4220 | 4301 | fn cIsInteger(qt: clang.QualType) bool { |
| 4221 | 4302 | return cIsSignedInteger(qt) or cIsUnsignedInteger(qt); |
| 4222 | 4303 | } |