| ... | ... | @@ -710,6 +710,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 710 | 710 | return addTopLevelDecl(c, fn_name, &proto_node.base); |
| 711 | 711 | } |
| 712 | 712 | |
| 713 | fn transQualTypeMaybeInitialized(rp: RestorePoint, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!*ast.Node { |
| 714 | return if (decl_init) |init_expr| |
| 715 | transQualTypeInitialized(rp, qt, init_expr, loc) |
| 716 | else |
| 717 | transQualType(rp, qt, loc); |
| 718 | } |
| 713 | 719 | /// if mangled_name is not null, this var decl was declared in a block scope. |
| 714 | 720 | fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void { |
| 715 | 721 | const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); |
| ... | ... | @@ -734,6 +740,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 734 | 740 | const storage_class = var_decl.getStorageClass(); |
| 735 | 741 | const is_const = qual_type.isConstQualified(); |
| 736 | 742 | const has_init = var_decl.hasInit(); |
| 743 | const decl_init = var_decl.getInit(); |
| 737 | 744 | |
| 738 | 745 | // In C extern variables with initializers behave like Zig exports. |
| 739 | 746 | // extern int foo = 2; |
| ... | ... | @@ -755,8 +762,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 755 | 762 | const name_tok = try appendIdentifier(c, checked_name); |
| 756 | 763 | |
| 757 | 764 | _ = try appendToken(c, .Colon, ":"); |
| 758 | | const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) { |
| 759 | | error.UnsupportedType => { |
| 765 | |
| 766 | const type_node = transQualTypeMaybeInitialized(rp, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { |
| 767 | error.UnsupportedTranslation, error.UnsupportedType => { |
| 760 | 768 | return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); |
| 761 | 769 | }, |
| 762 | 770 | error.OutOfMemory => |e| return e, |
| ... | ... | @@ -770,17 +778,22 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 770 | 778 | // with the variable type. |
| 771 | 779 | if (has_init) { |
| 772 | 780 | eq_tok = try appendToken(c, .Equal, "="); |
| 773 | | init_node = if (var_decl.getInit()) |expr| |
| 774 | | transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { |
| 781 | if (decl_init) |expr| { |
| 782 | const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) |
| 783 | transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), type_node) |
| 784 | else |
| 785 | transExprCoercing(rp, scope, expr, .used, .r_value); |
| 786 | init_node = node_or_error catch |err| switch (err) { |
| 775 | 787 | error.UnsupportedTranslation, |
| 776 | 788 | error.UnsupportedType, |
| 777 | 789 | => { |
| 778 | 790 | return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{}); |
| 779 | 791 | }, |
| 780 | 792 | error.OutOfMemory => |e| return e, |
| 781 | | } |
| 782 | | else |
| 783 | | try transCreateNodeUndefinedLiteral(c); |
| 793 | }; |
| 794 | } else { |
| 795 | init_node = try transCreateNodeUndefinedLiteral(c); |
| 796 | } |
| 784 | 797 | } else if (storage_class != .Extern) { |
| 785 | 798 | eq_tok = try appendToken(c, .Equal, "="); |
| 786 | 799 | // The C language specification states that variables with static or threadlocal |
| ... | ... | @@ -1620,6 +1633,7 @@ fn transDeclStmtOne( |
| 1620 | 1633 | switch (decl.getKind()) { |
| 1621 | 1634 | .Var => { |
| 1622 | 1635 | const var_decl = @ptrCast(*const clang.VarDecl, decl); |
| 1636 | const decl_init = var_decl.getInit(); |
| 1623 | 1637 | |
| 1624 | 1638 | const qual_type = var_decl.getTypeSourceInfo_getType(); |
| 1625 | 1639 | const name = try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); |
| ... | ... | @@ -1643,11 +1657,14 @@ fn transDeclStmtOne( |
| 1643 | 1657 | |
| 1644 | 1658 | _ = try appendToken(c, .Colon, ":"); |
| 1645 | 1659 | const loc = decl.getLocation(); |
| 1646 | | const type_node = try transQualType(rp, qual_type, loc); |
| 1660 | const type_node = try transQualTypeMaybeInitialized(rp, qual_type, decl_init, loc); |
| 1647 | 1661 | |
| 1648 | 1662 | const eq_token = try appendToken(c, .Equal, "="); |
| 1649 | | var init_node = if (var_decl.getInit()) |expr| |
| 1650 | | try transExprCoercing(rp, scope, expr, .used, .r_value) |
| 1663 | var init_node = if (decl_init) |expr| |
| 1664 | if (expr.getStmtClass() == .StringLiteralClass) |
| 1665 | try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) |
| 1666 | else |
| 1667 | try transExprCoercing(rp, scope, expr, .used, .r_value) |
| 1651 | 1668 | else |
| 1652 | 1669 | try transCreateNodeUndefinedLiteral(c); |
| 1653 | 1670 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { |
| ... | ... | @@ -1740,7 +1757,7 @@ fn transImplicitCastExpr( |
| 1740 | 1757 | return maybeSuppressResult(rp, scope, result_used, sub_expr_node); |
| 1741 | 1758 | }, |
| 1742 | 1759 | .ArrayToPointerDecay => { |
| 1743 | | if (exprIsStringLiteral(sub_expr)) { |
| 1760 | if (exprIsNarrowStringLiteral(sub_expr)) { |
| 1744 | 1761 | const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); |
| 1745 | 1762 | return maybeSuppressResult(rp, scope, result_used, sub_expr_node); |
| 1746 | 1763 | } |
| ... | ... | @@ -1841,17 +1858,20 @@ fn exprIsBooleanType(expr: *const clang.Expr) bool { |
| 1841 | 1858 | return qualTypeIsBoolean(expr.getType()); |
| 1842 | 1859 | } |
| 1843 | 1860 | |
| 1844 | | fn exprIsStringLiteral(expr: *const clang.Expr) bool { |
| 1861 | fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { |
| 1845 | 1862 | switch (expr.getStmtClass()) { |
| 1846 | | .StringLiteralClass => return true, |
| 1863 | .StringLiteralClass => { |
| 1864 | const string_lit = @ptrCast(*const clang.StringLiteral, expr); |
| 1865 | return string_lit.getCharByteWidth() == 1; |
| 1866 | }, |
| 1847 | 1867 | .PredefinedExprClass => return true, |
| 1848 | 1868 | .UnaryOperatorClass => { |
| 1849 | 1869 | const op_expr = @ptrCast(*const clang.UnaryOperator, expr).getSubExpr(); |
| 1850 | | return exprIsStringLiteral(op_expr); |
| 1870 | return exprIsNarrowStringLiteral(op_expr); |
| 1851 | 1871 | }, |
| 1852 | 1872 | .ParenExprClass => { |
| 1853 | 1873 | const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr(); |
| 1854 | | return exprIsStringLiteral(op_expr); |
| 1874 | return exprIsNarrowStringLiteral(op_expr); |
| 1855 | 1875 | }, |
| 1856 | 1876 | else => return false, |
| 1857 | 1877 | } |
| ... | ... | @@ -2049,6 +2069,71 @@ fn transStringLiteral( |
| 2049 | 2069 | } |
| 2050 | 2070 | } |
| 2051 | 2071 | |
| 2072 | /// Parse the size of an array back out from an ast Node. |
| 2073 | fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { |
| 2074 | if (node.castTag(.ArrayType)) |array| { |
| 2075 | if (array.len_expr.castTag(.IntegerLiteral)) |int_lit| { |
| 2076 | const tok = tokenSlice(c, int_lit.token); |
| 2077 | return std.fmt.parseUnsigned(usize, tok, 10) catch error.UnsupportedTranslation; |
| 2078 | } |
| 2079 | } |
| 2080 | return error.UnsupportedTranslation; |
| 2081 | } |
| 2082 | |
| 2083 | /// Translate a string literal to an array of integers. Used when an |
| 2084 | /// array is initialized from a string literal. `target_node` is the |
| 2085 | /// array being initialized. If the string literal is larger than the |
| 2086 | /// array, truncate the string. If the array is larger than the string |
| 2087 | /// literal, pad the array with 0's |
| 2088 | fn transStringLiteralAsArray( |
| 2089 | rp: RestorePoint, |
| 2090 | scope: *Scope, |
| 2091 | stmt: *const clang.StringLiteral, |
| 2092 | target_node: *ast.Node, |
| 2093 | ) TransError!*ast.Node { |
| 2094 | const array_size = try zigArraySize(rp.c, target_node); |
| 2095 | const str_length = stmt.getLength(); |
| 2096 | |
| 2097 | const expr_base = @ptrCast(*const clang.Expr, stmt); |
| 2098 | const ty = expr_base.getType().getTypePtr(); |
| 2099 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); |
| 2100 | |
| 2101 | const ty_node = try rp.c.arena.create(ast.Node.ArrayType); |
| 2102 | const op_token = try appendToken(rp.c, .LBracket, "["); |
| 2103 | const len_expr = try transCreateNodeInt(rp.c, array_size); |
| 2104 | _ = try appendToken(rp.c, .RBracket, "]"); |
| 2105 | |
| 2106 | ty_node.* = .{ |
| 2107 | .op_token = op_token, |
| 2108 | .rhs = try transQualType(rp, const_arr_ty.getElementType(), expr_base.getBeginLoc()), |
| 2109 | .len_expr = len_expr, |
| 2110 | }; |
| 2111 | _ = try appendToken(rp.c, .LBrace, "{"); |
| 2112 | var init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, array_size); |
| 2113 | init_node.* = .{ |
| 2114 | .lhs = &ty_node.base, |
| 2115 | .rtoken = undefined, |
| 2116 | .list_len = array_size, |
| 2117 | }; |
| 2118 | const init_list = init_node.list(); |
| 2119 | |
| 2120 | var i: c_uint = 0; |
| 2121 | const kind = stmt.getKind(); |
| 2122 | const narrow = kind == .Ascii or kind == .UTF8; |
| 2123 | while (i < str_length and i < array_size) : (i += 1) { |
| 2124 | const code_unit = stmt.getCodeUnit(i); |
| 2125 | init_list[i] = try transCreateCharLitNode(rp.c, narrow, code_unit); |
| 2126 | _ = try appendToken(rp.c, .Comma, ","); |
| 2127 | } |
| 2128 | while (i < array_size) : (i += 1) { |
| 2129 | init_list[i] = try transCreateNodeInt(rp.c, 0); |
| 2130 | _ = try appendToken(rp.c, .Comma, ","); |
| 2131 | } |
| 2132 | init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); |
| 2133 | |
| 2134 | return &init_node.base; |
| 2135 | } |
| 2136 | |
| 2052 | 2137 | fn cIsEnum(qt: clang.QualType) bool { |
| 2053 | 2138 | return qt.getCanonicalType().getTypeClass() == .Enum; |
| 2054 | 2139 | } |
| ... | ... | @@ -2343,6 +2428,18 @@ fn transCreateNodeArrayType( |
| 2343 | 2428 | return &node.base; |
| 2344 | 2429 | } |
| 2345 | 2430 | |
| 2431 | fn transCreateEmptyArray(rp: RestorePoint, loc: clang.SourceLocation, ty: *const clang.Type) TransError!*ast.Node { |
| 2432 | const ty_node = try transCreateNodeArrayType(rp, loc, ty, 0); |
| 2433 | _ = try appendToken(rp.c, .LBrace, "{"); |
| 2434 | const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 0); |
| 2435 | filler_init_node.* = .{ |
| 2436 | .lhs = ty_node, |
| 2437 | .rtoken = try appendToken(rp.c, .RBrace, "}"), |
| 2438 | .list_len = 0, |
| 2439 | }; |
| 2440 | return &filler_init_node.base; |
| 2441 | } |
| 2442 | |
| 2346 | 2443 | fn transInitListExprArray( |
| 2347 | 2444 | rp: RestorePoint, |
| 2348 | 2445 | scope: *Scope, |
| ... | ... | @@ -2360,6 +2457,10 @@ fn transInitListExprArray( |
| 2360 | 2457 | const all_count = size_ap_int.getLimitedValue(math.maxInt(usize)); |
| 2361 | 2458 | const leftover_count = all_count - init_count; |
| 2362 | 2459 | |
| 2460 | if (all_count == 0) { |
| 2461 | return transCreateEmptyArray(rp, loc, child_qt.getTypePtr()); |
| 2462 | } |
| 2463 | |
| 2363 | 2464 | var init_node: *ast.Node.ArrayInitializer = undefined; |
| 2364 | 2465 | var cat_tok: ast.TokenIndex = undefined; |
| 2365 | 2466 | if (init_count != 0) { |
| ... | ... | @@ -2934,6 +3035,21 @@ fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.Prede |
| 2934 | 3035 | return transStringLiteral(rp, scope, expr.getFunctionName(), used); |
| 2935 | 3036 | } |
| 2936 | 3037 | |
| 3038 | fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!*ast.Node { |
| 3039 | const node = try c.arena.create(ast.Node.OneToken); |
| 3040 | node.* = .{ |
| 3041 | .base = .{ .tag = .CharLiteral }, |
| 3042 | .token = undefined, |
| 3043 | }; |
| 3044 | if (narrow) { |
| 3045 | const val_array = [_]u8{@intCast(u8, val)}; |
| 3046 | node.token = try appendTokenFmt(c, .CharLiteral, "'{}'", .{std.zig.fmtEscapes(&val_array)}); |
| 3047 | } else { |
| 3048 | node.token = try appendTokenFmt(c, .CharLiteral, "'\\u{{{x}}}'", .{val}); |
| 3049 | } |
| 3050 | return &node.base; |
| 3051 | } |
| 3052 | |
| 2937 | 3053 | fn transCharLiteral( |
| 2938 | 3054 | rp: RestorePoint, |
| 2939 | 3055 | scope: *Scope, |
| ... | ... | @@ -2943,33 +3059,14 @@ fn transCharLiteral( |
| 2943 | 3059 | ) TransError!*ast.Node { |
| 2944 | 3060 | const kind = stmt.getKind(); |
| 2945 | 3061 | const val = stmt.getValue(); |
| 2946 | | const int_lit_node = switch (kind) { |
| 2947 | | .Ascii, .UTF8 => blk: { |
| 2948 | | if (kind == .Ascii) { |
| 2949 | | // C has a somewhat obscure feature called multi-character character |
| 2950 | | // constant |
| 2951 | | if (val > 255) |
| 2952 | | break :blk try transCreateNodeInt(rp.c, val); |
| 2953 | | } |
| 2954 | | const val_array = [_]u8 { @intCast(u8, val) }; |
| 2955 | | const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{std.zig.fmtEscapes(&val_array)}); |
| 2956 | | const node = try rp.c.arena.create(ast.Node.OneToken); |
| 2957 | | node.* = .{ |
| 2958 | | .base = .{ .tag = .CharLiteral }, |
| 2959 | | .token = token, |
| 2960 | | }; |
| 2961 | | break :blk &node.base; |
| 2962 | | }, |
| 2963 | | .Wide, .UTF16, .UTF32 => blk: { |
| 2964 | | const token = try appendTokenFmt(rp.c, .CharLiteral, "'\\u{{{x}}}'", .{val}); |
| 2965 | | const node = try rp.c.arena.create(ast.Node.OneToken); |
| 2966 | | node.* = .{ |
| 2967 | | .base = .{ .tag = .CharLiteral }, |
| 2968 | | .token = token, |
| 2969 | | }; |
| 2970 | | break :blk &node.base; |
| 2971 | | }, |
| 2972 | | }; |
| 3062 | const narrow = kind == .Ascii or kind == .UTF8; |
| 3063 | // C has a somewhat obscure feature called multi-character character constant |
| 3064 | // e.g. 'abcd' |
| 3065 | const int_lit_node = if (kind == .Ascii and val > 255) |
| 3066 | try transCreateNodeInt(rp.c, val) |
| 3067 | else |
| 3068 | try transCreateCharLitNode(rp.c, narrow, val); |
| 3069 | |
| 2973 | 3070 | if (suppress_as == .no_as) { |
| 2974 | 3071 | return maybeSuppressResult(rp, scope, result_used, int_lit_node); |
| 2975 | 3072 | } |
| ... | ... | @@ -3891,6 +3988,38 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void { |
| 3891 | 3988 | _ = try c.global_scope.sym_table.put(name, decl_node); |
| 3892 | 3989 | } |
| 3893 | 3990 | |
| 3991 | /// Translate a qual type for a variable with an initializer. The initializer |
| 3992 | /// only matters for incomplete arrays, since the size of the array is determined |
| 3993 | /// by the size of the initializer |
| 3994 | fn transQualTypeInitialized( |
| 3995 | rp: RestorePoint, |
| 3996 | qt: clang.QualType, |
| 3997 | decl_init: *const clang.Expr, |
| 3998 | source_loc: clang.SourceLocation, |
| 3999 | ) TypeError!*ast.Node { |
| 4000 | const ty = qt.getTypePtr(); |
| 4001 | if (ty.getTypeClass() == .IncompleteArray) { |
| 4002 | const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); |
| 4003 | const elem_ty = incomplete_array_ty.getElementType().getTypePtr(); |
| 4004 | |
| 4005 | switch (decl_init.getStmtClass()) { |
| 4006 | .StringLiteralClass => { |
| 4007 | const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); |
| 4008 | const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator |
| 4009 | const array_size = @intCast(usize, string_lit_size); |
| 4010 | return transCreateNodeArrayType(rp, source_loc, elem_ty, array_size); |
| 4011 | }, |
| 4012 | .InitListExprClass => { |
| 4013 | const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); |
| 4014 | const size = init_expr.getNumInits(); |
| 4015 | return transCreateNodeArrayType(rp, source_loc, elem_ty, size); |
| 4016 | }, |
| 4017 | else => {}, |
| 4018 | } |
| 4019 | } |
| 4020 | return transQualType(rp, qt, source_loc); |
| 4021 | } |
| 4022 | |
| 3894 | 4023 | fn transQualType(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!*ast.Node { |
| 3895 | 4024 | return transType(rp, qt.getTypePtr(), source_loc); |
| 3896 | 4025 | } |