| author | |
| committer | |
| log | c9ad1b51993e45942e38079325dcb7b5ce41e877 |
| tree | cd45690414db3cca09af08c918a415f1d752a533 |
| parent | bcb534c295d5cc6fd63caa570cc08e6b148a507c |
25 files changed, 881 insertions(+), 460 deletions(-)
lib/compiler/aro/aro/Type.zig+3-1| ... | @@ -1142,12 +1142,14 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { | ... | @@ -1142,12 +1142,14 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { |
| 1142 | }; | 1142 | }; |
| 1143 | } | 1143 | } |
| 1144 | 1144 | ||
| 1145 | pub const QualHandling = enum { standard, preserve_quals }; | ||
| 1146 | |||
| 1145 | /// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply | 1147 | /// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply |
| 1146 | /// return it. Otherwise, determine the actual qualified type. | 1148 | /// return it. Otherwise, determine the actual qualified type. |
| 1147 | /// The `qual_handling` parameter can be used to return the full set of qualifiers | 1149 | /// The `qual_handling` parameter can be used to return the full set of qualifiers |
| 1148 | /// added by typeof() operations, which is useful when determining the elemType of | 1150 | /// added by typeof() operations, which is useful when determining the elemType of |
| 1149 | /// arrays and pointers. | 1151 | /// arrays and pointers. |
| 1150 | pub fn canonicalize(ty: Type, qual_handling: enum { standard, preserve_quals }) Type { | 1152 | pub fn canonicalize(ty: Type, qual_handling: QualHandling) Type { |
| 1151 | var cur = ty; | 1153 | var cur = ty; |
| 1152 | if (cur.specifier == .attributed) { | 1154 | if (cur.specifier == .attributed) { |
| 1153 | cur = cur.data.attributed.base; | 1155 | cur = cur.data.attributed.base; |
lib/compiler/aro_translate_c.zig+404-89| ... | @@ -52,18 +52,17 @@ fn getMangle(c: *Context) u32 { | ... | @@ -52,18 +52,17 @@ fn getMangle(c: *Context) u32 { |
| 52 | return c.mangle_count; | 52 | return c.mangle_count; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | /// Convert a clang source location to a file:line:column string | 55 | /// Convert an aro TokenIndex to a 'file:line:column' string |
| 56 | fn locStr(c: *Context, loc: TokenIndex) ![]const u8 { | 56 | fn locStr(c: *Context, tok_idx: TokenIndex) ![]const u8 { |
| 57 | _ = c; | 57 | const token_loc = c.tree.tokens.items(.loc)[tok_idx]; |
| 58 | _ = loc; | 58 | const source = c.comp.getSource(token_loc.id); |
| 59 | // const spelling_loc = c.source_manager.getSpellingLoc(loc); | 59 | const line_col = source.lineCol(token_loc); |
| 60 | // const filename_c = c.source_manager.getFilename(spelling_loc); | 60 | const filename = source.path; |
| 61 | // const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)"); | 61 | |
| 62 | 62 | const line = source.physicalLine(token_loc); | |
| 63 | // const line = c.source_manager.getSpellingLineNumber(spelling_loc); | 63 | const col = line_col.col; |
| 64 | // const column = c.source_manager.getSpellingColumnNumber(spelling_loc); | 64 | |
| 65 | // return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); | 65 | return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, col }); |
| 66 | return "somewhere"; | ||
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode { | 68 | fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode { |
| ... | @@ -184,26 +183,30 @@ fn prepopulateGlobalNameTable(c: *Context) !void { | ... | @@ -184,26 +183,30 @@ fn prepopulateGlobalNameTable(c: *Context) !void { |
| 184 | const node_data = c.tree.nodes.items(.data); | 183 | const node_data = c.tree.nodes.items(.data); |
| 185 | for (c.tree.root_decls) |node| { | 184 | for (c.tree.root_decls) |node| { |
| 186 | const data = node_data[@intFromEnum(node)]; | 185 | const data = node_data[@intFromEnum(node)]; |
| 187 | const decl_name = switch (node_tags[@intFromEnum(node)]) { | 186 | switch (node_tags[@intFromEnum(node)]) { |
| 188 | .typedef => @panic("TODO"), | 187 | .typedef => @panic("TODO"), |
| 189 | 188 | ||
| 190 | .static_assert, | ||
| 191 | .struct_decl_two, | 189 | .struct_decl_two, |
| 192 | .union_decl_two, | 190 | .union_decl_two, |
| 193 | .struct_decl, | 191 | .struct_decl, |
| 194 | .union_decl, | 192 | .union_decl, |
| 195 | => blk: { | 193 | .struct_forward_decl, |
| 196 | const ty = node_types[@intFromEnum(node)]; | 194 | .union_forward_decl, |
| 197 | const name_id = ty.data.record.name; | ||
| 198 | break :blk c.mapper.lookup(name_id); | ||
| 199 | }, | ||
| 200 | |||
| 201 | .enum_decl_two, | 195 | .enum_decl_two, |
| 202 | .enum_decl, | 196 | .enum_decl, |
| 203 | => blk: { | 197 | .enum_forward_decl, |
| 204 | const ty = node_types[@intFromEnum(node)]; | 198 | => { |
| 205 | const name_id = ty.data.@"enum".name; | 199 | const raw_ty = node_types[@intFromEnum(node)]; |
| 206 | break :blk c.mapper.lookup(name_id); | 200 | const ty = raw_ty.canonicalize(.standard); |
| 201 | const name_id = if (ty.isRecord()) ty.data.record.name else ty.data.@"enum".name; | ||
| 202 | const decl_name = c.mapper.lookup(name_id); | ||
| 203 | const container_prefix = if (ty.is(.@"struct")) "struct" else if (ty.is(.@"union")) "union" else "enum"; | ||
| 204 | const prefixed_name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_prefix, decl_name }); | ||
| 205 | // `decl_name` and `prefixed_name` are the preferred names for this type. | ||
| 206 | // However, we can name it anything else if necessary, so these are "weak names". | ||
| 207 | try c.weak_global_names.ensureUnusedCapacity(c.gpa, 2); | ||
| 208 | c.weak_global_names.putAssumeCapacity(decl_name, {}); | ||
| 209 | c.weak_global_names.putAssumeCapacity(prefixed_name, {}); | ||
| 207 | }, | 210 | }, |
| 208 | 211 | ||
| 209 | .fn_proto, | 212 | .fn_proto, |
| ... | @@ -215,80 +218,256 @@ fn prepopulateGlobalNameTable(c: *Context) !void { | ... | @@ -215,80 +218,256 @@ fn prepopulateGlobalNameTable(c: *Context) !void { |
| 215 | .inline_fn_def, | 218 | .inline_fn_def, |
| 216 | .inline_static_fn_def, | 219 | .inline_static_fn_def, |
| 217 | .@"var", | 220 | .@"var", |
| 221 | .extern_var, | ||
| 218 | .static_var, | 222 | .static_var, |
| 219 | .threadlocal_var, | 223 | .threadlocal_var, |
| 220 | .threadlocal_static_var, | ||
| 221 | .extern_var, | ||
| 222 | .threadlocal_extern_var, | 224 | .threadlocal_extern_var, |
| 223 | => c.tree.tokSlice(data.decl.name), | 225 | .threadlocal_static_var, |
| 226 | => { | ||
| 227 | const decl_name = c.tree.tokSlice(data.decl.name); | ||
| 228 | try c.global_names.put(c.gpa, decl_name, {}); | ||
| 229 | }, | ||
| 224 | else => unreachable, | 230 | else => unreachable, |
| 225 | }; | 231 | } |
| 226 | try c.global_names.put(c.gpa, decl_name, {}); | ||
| 227 | } | 232 | } |
| 228 | } | 233 | } |
| 229 | 234 | ||
| 230 | fn transTopLevelDecls(c: *Context) !void { | 235 | fn transTopLevelDecls(c: *Context) !void { |
| 236 | for (c.tree.root_decls) |node| { | ||
| 237 | try transDecl(c, &c.global_scope.base, node); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | fn transDecl(c: *Context, scope: *Scope, decl: NodeIndex) !void { | ||
| 231 | const node_tags = c.tree.nodes.items(.tag); | 242 | const node_tags = c.tree.nodes.items(.tag); |
| 232 | const node_data = c.tree.nodes.items(.data); | 243 | const node_data = c.tree.nodes.items(.data); |
| 233 | for (c.tree.root_decls) |node| { | 244 | const data = node_data[@intFromEnum(decl)]; |
| 234 | const data = node_data[@intFromEnum(node)]; | 245 | switch (node_tags[@intFromEnum(decl)]) { |
| 235 | switch (node_tags[@intFromEnum(node)]) { | 246 | .typedef => { |
| 236 | .typedef => { | 247 | try transTypeDef(c, scope, decl); |
| 237 | try transTypeDef(c, &c.global_scope.base, node); | 248 | }, |
| 238 | }, | ||
| 239 | 249 | ||
| 240 | .static_assert, | 250 | .struct_decl_two, |
| 241 | .struct_decl_two, | 251 | .union_decl_two, |
| 242 | .union_decl_two, | 252 | => { |
| 243 | .struct_decl, | 253 | var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs }; |
| 244 | .union_decl, | 254 | var field_count: u2 = 0; |
| 245 | => { | 255 | if (fields[0] != .none) field_count += 1; |
| 246 | try transRecordDecl(c, &c.global_scope.base, node); | 256 | if (fields[1] != .none) field_count += 1; |
| 247 | }, | 257 | try transRecordDecl(c, scope, decl, fields[0..field_count]); |
| 258 | }, | ||
| 259 | .struct_decl, | ||
| 260 | .union_decl, | ||
| 261 | => { | ||
| 262 | const fields = c.tree.data[data.range.start..data.range.end]; | ||
| 263 | try transRecordDecl(c, scope, decl, fields); | ||
| 264 | }, | ||
| 248 | 265 | ||
| 249 | .enum_decl_two => { | 266 | .enum_decl_two => { |
| 250 | var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs }; | 267 | var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs }; |
| 251 | var field_count: u8 = 0; | 268 | var field_count: u8 = 0; |
| 252 | if (fields[0] != .none) field_count += 1; | 269 | if (fields[0] != .none) field_count += 1; |
| 253 | if (fields[1] != .none) field_count += 1; | 270 | if (fields[1] != .none) field_count += 1; |
| 254 | try transEnumDecl(c, &c.global_scope.base, node, fields[0..field_count]); | 271 | try transEnumDecl(c, scope, decl, fields[0..field_count]); |
| 255 | }, | 272 | }, |
| 256 | .enum_decl => { | 273 | .enum_decl => { |
| 257 | const fields = c.tree.data[data.range.start..data.range.end]; | 274 | const fields = c.tree.data[data.range.start..data.range.end]; |
| 258 | try transEnumDecl(c, &c.global_scope.base, node, fields); | 275 | try transEnumDecl(c, scope, decl, fields); |
| 259 | }, | 276 | }, |
| 260 | 277 | ||
| 261 | .fn_proto, | 278 | .enum_field_decl, |
| 262 | .static_fn_proto, | 279 | .record_field_decl, |
| 263 | .inline_fn_proto, | 280 | .indirect_record_field_decl, |
| 264 | .inline_static_fn_proto, | 281 | .struct_forward_decl, |
| 265 | .fn_def, | 282 | .union_forward_decl, |
| 266 | .static_fn_def, | 283 | .enum_forward_decl, |
| 267 | .inline_fn_def, | 284 | => return, |
| 268 | .inline_static_fn_def, | 285 | |
| 269 | => { | 286 | .fn_proto, |
| 270 | try transFnDecl(c, node); | 287 | .static_fn_proto, |
| 271 | }, | 288 | .inline_fn_proto, |
| 289 | .inline_static_fn_proto, | ||
| 290 | .fn_def, | ||
| 291 | .static_fn_def, | ||
| 292 | .inline_fn_def, | ||
| 293 | .inline_static_fn_def, | ||
| 294 | => { | ||
| 295 | try transFnDecl(c, decl); | ||
| 296 | }, | ||
| 272 | 297 | ||
| 273 | .@"var", | 298 | .@"var", |
| 274 | .static_var, | 299 | .extern_var, |
| 275 | .threadlocal_var, | 300 | .static_var, |
| 276 | .threadlocal_static_var, | 301 | .threadlocal_var, |
| 277 | .extern_var, | 302 | .threadlocal_extern_var, |
| 278 | .threadlocal_extern_var, | 303 | .threadlocal_static_var, |
| 279 | => { | 304 | => { |
| 280 | try transVarDecl(c, node, null); | 305 | try transVarDecl(c, decl, null); |
| 281 | }, | 306 | }, |
| 282 | else => unreachable, | 307 | else => unreachable, |
| 283 | } | ||
| 284 | } | 308 | } |
| 285 | } | 309 | } |
| 286 | 310 | ||
| 287 | fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void { | 311 | fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void { |
| 288 | @panic("TODO"); | 312 | @panic("TODO"); |
| 289 | } | 313 | } |
| 290 | fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void { | 314 | |
| 291 | @panic("TODO"); | 315 | fn mangleWeakGlobalName(c: *Context, want_name: []const u8) ![]const u8 { |
| 316 | var cur_name = want_name; | ||
| 317 | |||
| 318 | if (!c.weak_global_names.contains(want_name)) { | ||
| 319 | // This type wasn't noticed by the name detection pass, so nothing has been treating this as | ||
| 320 | // a weak global name. We must mangle it to avoid conflicts with locals. | ||
| 321 | cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() }); | ||
| 322 | } | ||
| 323 | |||
| 324 | while (c.global_names.contains(cur_name)) { | ||
| 325 | cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() }); | ||
| 326 | } | ||
| 327 | return cur_name; | ||
| 328 | } | ||
| 329 | |||
| 330 | fn transRecordDecl(c: *Context, scope: *Scope, record_node: NodeIndex, field_nodes: []const NodeIndex) Error!void { | ||
| 331 | const node_types = c.tree.nodes.items(.ty); | ||
| 332 | const raw_record_ty = node_types[@intFromEnum(record_node)]; | ||
| 333 | const record_decl = raw_record_ty.getRecord().?; | ||
| 334 | if (c.decl_table.get(@intFromPtr(record_decl))) |_| | ||
| 335 | return; // Avoid processing this decl twice | ||
| 336 | const toplevel = scope.id == .root; | ||
| 337 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; | ||
| 338 | |||
| 339 | const container_kind: ZigTag = if (raw_record_ty.is(.@"union")) .@"union" else .@"struct"; | ||
| 340 | const container_kind_name: []const u8 = @tagName(container_kind); | ||
| 341 | |||
| 342 | var is_unnamed = false; | ||
| 343 | var bare_name: []const u8 = c.mapper.lookup(record_decl.name); | ||
| 344 | var name = bare_name; | ||
| 345 | |||
| 346 | if (c.unnamed_typedefs.get(@intFromPtr(record_decl))) |typedef_name| { | ||
| 347 | bare_name = typedef_name; | ||
| 348 | name = typedef_name; | ||
| 349 | } else { | ||
| 350 | if (raw_record_ty.isAnonymousRecord(c.comp)) { | ||
| 351 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | ||
| 352 | is_unnamed = true; | ||
| 353 | } | ||
| 354 | name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); | ||
| 355 | if (toplevel and !is_unnamed) { | ||
| 356 | name = try mangleWeakGlobalName(c, name); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | if (!toplevel) name = try bs.makeMangledName(c, name); | ||
| 360 | try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl), name); | ||
| 361 | |||
| 362 | const is_pub = toplevel and !is_unnamed; | ||
| 363 | const init_node = blk: { | ||
| 364 | var fields = try std.ArrayList(ast.Payload.Record.Field).initCapacity(c.gpa, record_decl.fields.len); | ||
| 365 | defer fields.deinit(); | ||
| 366 | |||
| 367 | // TODO: Add support for flexible array field functions | ||
| 368 | var functions = std.ArrayList(ZigNode).init(c.gpa); | ||
| 369 | defer functions.deinit(); | ||
| 370 | |||
| 371 | var unnamed_field_count: u32 = 0; | ||
| 372 | |||
| 373 | // If a record doesn't have any attributes that would affect the alignment and | ||
| 374 | // layout, then we can just use a simple `extern` type. If it does have attributes, | ||
| 375 | // then we need to inspect the layout and assign an `align` value for each field. | ||
| 376 | const has_alignment_attributes = record_decl.field_attributes != null or | ||
| 377 | raw_record_ty.hasAttribute(.@"packed") or | ||
| 378 | raw_record_ty.hasAttribute(.aligned); | ||
| 379 | const head_field_alignment: ?c_uint = headFieldAlignment(record_decl); | ||
| 380 | |||
| 381 | // Iterate over field nodes so that we translate any type decls included in this record decl. | ||
| 382 | // TODO: Move this logic into `fn transType()` instead of handling decl translation here. | ||
| 383 | for (field_nodes) |field_node| { | ||
| 384 | const field_raw_ty = node_types[@intFromEnum(field_node)]; | ||
| 385 | if (field_raw_ty.isEnumOrRecord()) try transDecl(c, scope, field_node); | ||
| 386 | } | ||
| 387 | |||
| 388 | for (record_decl.fields, 0..) |field, field_index| { | ||
| 389 | const field_loc = field.name_tok; | ||
| 390 | |||
| 391 | // Demote record to opaque if it contains a bitfield | ||
| 392 | if (!field.isRegularField()) { | ||
| 393 | try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl), {}); | ||
| 394 | try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); | ||
| 395 | break :blk ZigTag.opaque_literal.init(); | ||
| 396 | } | ||
| 397 | |||
| 398 | var field_name = c.mapper.lookup(field.name); | ||
| 399 | if (!field.isNamed()) { | ||
| 400 | field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); | ||
| 401 | unnamed_field_count += 1; | ||
| 402 | } | ||
| 403 | const field_type = transType(c, scope, field.ty, .preserve_quals, field_loc) catch |err| switch (err) { | ||
| 404 | error.UnsupportedType => { | ||
| 405 | try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl), {}); | ||
| 406 | try warn(c, scope, 0, "{s} demoted to opaque type - unable to translate type of field {s}", .{ | ||
| 407 | container_kind_name, | ||
| 408 | field_name, | ||
| 409 | }); | ||
| 410 | break :blk ZigTag.opaque_literal.init(); | ||
| 411 | }, | ||
| 412 | else => |e| return e, | ||
| 413 | }; | ||
| 414 | |||
| 415 | const field_alignment = if (has_alignment_attributes) | ||
| 416 | alignmentForField(record_decl, head_field_alignment, field_index) | ||
| 417 | else | ||
| 418 | null; | ||
| 419 | |||
| 420 | // C99 introduced designated initializers for structs. Omitted fields are implicitly | ||
| 421 | // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero | ||
| 422 | // values for translated struct fields permits Zig code to comfortably use such an API. | ||
| 423 | const default_value = if (container_kind == .@"struct") | ||
| 424 | try ZigTag.std_mem_zeroes.create(c.arena, field_type) | ||
| 425 | else | ||
| 426 | null; | ||
| 427 | |||
| 428 | fields.appendAssumeCapacity(.{ | ||
| 429 | .name = field_name, | ||
| 430 | .type = field_type, | ||
| 431 | .alignment = field_alignment, | ||
| 432 | .default_value = default_value, | ||
| 433 | }); | ||
| 434 | } | ||
| 435 | |||
| 436 | const record_payload = try c.arena.create(ast.Payload.Record); | ||
| 437 | record_payload.* = .{ | ||
| 438 | .base = .{ .tag = container_kind }, | ||
| 439 | .data = .{ | ||
| 440 | .layout = .@"extern", | ||
| 441 | .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), | ||
| 442 | .functions = try c.arena.dupe(ZigNode, functions.items), | ||
| 443 | .variables = &.{}, | ||
| 444 | }, | ||
| 445 | }; | ||
| 446 | break :blk ZigNode.initPayload(&record_payload.base); | ||
| 447 | }; | ||
| 448 | |||
| 449 | const payload = try c.arena.create(ast.Payload.SimpleVarDecl); | ||
| 450 | payload.* = .{ | ||
| 451 | .base = .{ .tag = ([2]ZigTag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] }, | ||
| 452 | .data = .{ | ||
| 453 | .name = name, | ||
| 454 | .init = init_node, | ||
| 455 | }, | ||
| 456 | }; | ||
| 457 | const node = ZigNode.initPayload(&payload.base); | ||
| 458 | if (toplevel) { | ||
| 459 | try addTopLevelDecl(c, name, node); | ||
| 460 | // Only add the alias if the name is available *and* it was caught by | ||
| 461 | // name detection. Don't bother performing a weak mangle, since a | ||
| 462 | // mangled name is of no real use here. | ||
| 463 | if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name)) | ||
| 464 | try c.alias_list.append(.{ .alias = bare_name, .name = name }); | ||
| 465 | } else { | ||
| 466 | try scope.appendNode(node); | ||
| 467 | if (node.tag() != .pub_var_simple) { | ||
| 468 | try bs.discardVariable(c, name); | ||
| 469 | } | ||
| 470 | } | ||
| 292 | } | 471 | } |
| 293 | 472 | ||
| 294 | fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void { | 473 | fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void { |
| ... | @@ -419,7 +598,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: | ... | @@ -419,7 +598,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: |
| 419 | enum_val_name = try bs.makeMangledName(c, enum_val_name); | 598 | enum_val_name = try bs.makeMangledName(c, enum_val_name); |
| 420 | } | 599 | } |
| 421 | 600 | ||
| 422 | const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, field.name_tok) catch |err| switch (err) { | 601 | const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, .standard, field.name_tok) catch |err| switch (err) { |
| 423 | error.UnsupportedType => null, | 602 | error.UnsupportedType => null, |
| 424 | else => |e| return e, | 603 | else => |e| return e, |
| 425 | }; | 604 | }; |
| ... | @@ -439,7 +618,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: | ... | @@ -439,7 +618,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: |
| 439 | } | 618 | } |
| 440 | } | 619 | } |
| 441 | 620 | ||
| 442 | break :blk transType(c, scope, ty.data.@"enum".tag_ty, 0) catch |err| switch (err) { | 621 | break :blk transType(c, scope, ty.data.@"enum".tag_ty, .standard, 0) catch |err| switch (err) { |
| 443 | error.UnsupportedType => { | 622 | error.UnsupportedType => { |
| 444 | return failDecl(c, 0, name, "unable to translate enum integer type", .{}); | 623 | return failDecl(c, 0, name, "unable to translate enum integer type", .{}); |
| 445 | }, | 624 | }, |
| ... | @@ -472,8 +651,8 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: | ... | @@ -472,8 +651,8 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: |
| 472 | } | 651 | } |
| 473 | } | 652 | } |
| 474 | 653 | ||
| 475 | fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode { | 654 | fn transType(c: *Context, scope: *Scope, raw_ty: Type, qual_handling: Type.QualHandling, source_loc: TokenIndex) TypeError!ZigNode { |
| 476 | const ty = raw_ty.canonicalize(.standard); | 655 | const ty = raw_ty.canonicalize(qual_handling); |
| 477 | switch (ty.specifier) { | 656 | switch (ty.specifier) { |
| 478 | .void => return ZigTag.type.create(c.arena, "anyopaque"), | 657 | .void => return ZigTag.type.create(c.arena, "anyopaque"), |
| 479 | .bool => return ZigTag.type.create(c.arena, "bool"), | 658 | .bool => return ZigTag.type.create(c.arena, "bool"), |
| ... | @@ -496,16 +675,152 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) T | ... | @@ -496,16 +675,152 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) T |
| 496 | .long_double => return ZigTag.type.create(c.arena, "c_longdouble"), | 675 | .long_double => return ZigTag.type.create(c.arena, "c_longdouble"), |
| 497 | .float80 => return ZigTag.type.create(c.arena, "f80"), | 676 | .float80 => return ZigTag.type.create(c.arena, "f80"), |
| 498 | .float128 => return ZigTag.type.create(c.arena, "f128"), | 677 | .float128 => return ZigTag.type.create(c.arena, "f128"), |
| 678 | .@"enum" => @panic("TODO"), | ||
| 679 | .pointer, | ||
| 680 | .unspecified_variable_len_array, | ||
| 681 | .array, | ||
| 682 | .static_array, | ||
| 683 | .incomplete_array, | ||
| 684 | => @panic("TODO"), | ||
| 499 | .func, | 685 | .func, |
| 500 | .var_args_func, | 686 | .var_args_func, |
| 501 | .old_style_func, | 687 | .old_style_func, |
| 502 | => return transFnType(c, scope, raw_ty, ty, source_loc, .{}), | 688 | => return transFnType(c, scope, ty, ty, source_loc, .{}), |
| 689 | .@"struct", | ||
| 690 | .@"union", | ||
| 691 | => { | ||
| 692 | var trans_scope = scope; | ||
| 693 | if (ty.isAnonymousRecord(c.comp)) { | ||
| 694 | const record_decl = ty.data.record; | ||
| 695 | const name_id = c.mapper.lookup(record_decl.name); | ||
| 696 | if (c.weak_global_names.contains(name_id)) trans_scope = &c.global_scope.base; | ||
| 697 | } | ||
| 698 | const name = c.decl_table.get(@intFromPtr(ty.data.record)).?; | ||
| 699 | return ZigTag.identifier.create(c.arena, name); | ||
| 700 | }, | ||
| 701 | .attributed, | ||
| 702 | .typeof_type, | ||
| 703 | .typeof_expr, | ||
| 704 | => unreachable, | ||
| 503 | else => return error.UnsupportedType, | 705 | else => return error.UnsupportedType, |
| 504 | } | 706 | } |
| 505 | } | 707 | } |
| 506 | 708 | ||
| 507 | fn zigAlignment(bit_alignment: u29) u32 { | 709 | /// Look ahead through the fields of the record to determine what the alignment of the record |
| 508 | return bit_alignment / 8; | 710 | /// would be without any align/packed/etc. attributes. This helps us determine whether or not |
| 711 | /// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just | ||
| 712 | /// pedantically assign those fields the same alignment as the parent's pointer alignment, | ||
| 713 | /// but this helps the generated code to be a little less verbose. | ||
| 714 | fn headFieldAlignment(record_decl: *const Type.Record) ?c_uint { | ||
| 715 | const bits_per_byte = 8; | ||
| 716 | const parent_ptr_alignment_bits = record_decl.type_layout.pointer_alignment_bits; | ||
| 717 | const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte; | ||
| 718 | var max_field_alignment_bits: u64 = 0; | ||
| 719 | for (record_decl.fields) |field| { | ||
| 720 | if (field.ty.getRecord()) |field_record_decl| { | ||
| 721 | const child_record_alignment = field_record_decl.type_layout.field_alignment_bits; | ||
| 722 | if (child_record_alignment > max_field_alignment_bits) | ||
| 723 | max_field_alignment_bits = child_record_alignment; | ||
| 724 | } else { | ||
| 725 | const field_size = field.layout.size_bits; | ||
| 726 | if (field_size > max_field_alignment_bits) | ||
| 727 | max_field_alignment_bits = field_size; | ||
| 728 | } | ||
| 729 | } | ||
| 730 | if (max_field_alignment_bits != parent_ptr_alignment_bits) { | ||
| 731 | return parent_ptr_alignment; | ||
| 732 | } else { | ||
| 733 | return null; | ||
| 734 | } | ||
| 735 | } | ||
| 736 | |||
| 737 | /// This function returns a ?c_uint to match Clang's behaviour of using c_uint. | ||
| 738 | /// This can be changed to a u29 after the Clang frontend for translate-c is removed. | ||
| 739 | fn alignmentForField( | ||
| 740 | record_decl: *const Type.Record, | ||
| 741 | head_field_alignment: ?c_uint, | ||
| 742 | field_index: usize, | ||
| 743 | ) ?c_uint { | ||
| 744 | const fields = record_decl.fields; | ||
| 745 | assert(fields.len != 0); | ||
| 746 | const field = fields[field_index]; | ||
| 747 | |||
| 748 | const bits_per_byte = 8; | ||
| 749 | const parent_ptr_alignment_bits = record_decl.type_layout.pointer_alignment_bits; | ||
| 750 | const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte; | ||
| 751 | |||
| 752 | // bitfields aren't supported yet. Until support is added, records with bitfields | ||
| 753 | // should be demoted to opaque, and this function shouldn't be called for them. | ||
| 754 | if (!field.isRegularField()) { | ||
| 755 | @panic("TODO: add bitfield support for records"); | ||
| 756 | } | ||
| 757 | |||
| 758 | const field_offset_bits: u64 = field.layout.offset_bits; | ||
| 759 | const field_size_bits: u64 = field.layout.size_bits; | ||
| 760 | |||
| 761 | // Fields with zero width always have an alignment of 1 | ||
| 762 | if (field_size_bits == 0) { | ||
| 763 | return 1; | ||
| 764 | } | ||
| 765 | |||
| 766 | // Fields with 0 offset inherit the parent's pointer alignment. | ||
| 767 | if (field_offset_bits == 0) { | ||
| 768 | return head_field_alignment; | ||
| 769 | } | ||
| 770 | |||
| 771 | // Records have a natural alignment when used as a field, and their size is | ||
| 772 | // a multiple of this alignment value. For all other types, the natural alignment | ||
| 773 | // is their size. | ||
| 774 | const field_natural_alignment_bits: u64 = if (field.ty.getRecord()) |record| record.type_layout.field_alignment_bits else field_size_bits; | ||
| 775 | const rem_bits = field_offset_bits % field_natural_alignment_bits; | ||
| 776 | |||
| 777 | // If there's a remainder, then the alignment is smaller than the field's | ||
| 778 | // natural alignment | ||
| 779 | if (rem_bits > 0) { | ||
| 780 | const rem_alignment = rem_bits / bits_per_byte; | ||
| 781 | if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) { | ||
| 782 | const actual_alignment = @min(rem_alignment, parent_ptr_alignment); | ||
| 783 | return @as(c_uint, @truncate(actual_alignment)); | ||
| 784 | } else { | ||
| 785 | return 1; | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | // A field may have an offset which positions it to be naturally aligned, but the | ||
| 790 | // parent's pointer alignment determines if this is actually true, so we take the minimum | ||
| 791 | // value. | ||
| 792 | // For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural | ||
| 793 | // alignment, but if the parent pointer alignment is 2, then the actual alignment of the | ||
| 794 | // float is 2. | ||
| 795 | const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte; | ||
| 796 | const offset_alignment = field_offset_bits / bits_per_byte; | ||
| 797 | const possible_alignment = @min(parent_ptr_alignment, offset_alignment); | ||
| 798 | if (possible_alignment == field_natural_alignment) { | ||
| 799 | return null; | ||
| 800 | } else if (possible_alignment < field_natural_alignment) { | ||
| 801 | if (std.math.isPowerOfTwo(possible_alignment)) { | ||
| 802 | return possible_alignment; | ||
| 803 | } else { | ||
| 804 | return 1; | ||
| 805 | } | ||
| 806 | } else { // possible_alignment > field_natural_alignment | ||
| 807 | // Here, the field is positioned be at a higher alignment than it's natural alignment. This means we | ||
| 808 | // need to determine whether it's a specified alignment. We can determine that from the padding preceding | ||
| 809 | // the field. | ||
| 810 | const padding_from_prev_field: u64 = blk: { | ||
| 811 | if (field_offset_bits != 0) { | ||
| 812 | const previous_field = fields[field_index - 1]; | ||
| 813 | break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits; | ||
| 814 | } else { | ||
| 815 | break :blk 0; | ||
| 816 | } | ||
| 817 | }; | ||
| 818 | if (padding_from_prev_field < field_natural_alignment_bits) { | ||
| 819 | return null; | ||
| 820 | } else { | ||
| 821 | return possible_alignment; | ||
| 822 | } | ||
| 823 | } | ||
| 509 | } | 824 | } |
| 510 | 825 | ||
| 511 | const FnProtoContext = struct { | 826 | const FnProtoContext = struct { |
| ... | @@ -536,7 +851,7 @@ fn transFnType( | ... | @@ -536,7 +851,7 @@ fn transFnType( |
| 536 | else | 851 | else |
| 537 | c.mapper.lookup(param_info.name); | 852 | c.mapper.lookup(param_info.name); |
| 538 | 853 | ||
| 539 | const type_node = try transType(c, scope, param_ty, param_info.name_tok); | 854 | const type_node = try transType(c, scope, param_ty, .standard, param_info.name_tok); |
| 540 | param_node.* = .{ | 855 | param_node.* = .{ |
| 541 | .is_noalias = is_noalias, | 856 | .is_noalias = is_noalias, |
| 542 | .name = param_name, | 857 | .name = param_name, |
| ... | @@ -551,7 +866,7 @@ fn transFnType( | ... | @@ -551,7 +866,7 @@ fn transFnType( |
| 551 | break :blk null; | 866 | break :blk null; |
| 552 | }; | 867 | }; |
| 553 | 868 | ||
| 554 | const alignment = if (raw_ty.requestedAlignment(c.comp)) |alignment| zigAlignment(alignment) else null; | 869 | const alignment: ?c_uint = raw_ty.requestedAlignment(c.comp) orelse null; |
| 555 | 870 | ||
| 556 | const explicit_callconv = null; | 871 | const explicit_callconv = null; |
| 557 | // const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc; | 872 | // const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc; |
| ... | @@ -565,7 +880,7 @@ fn transFnType( | ... | @@ -565,7 +880,7 @@ fn transFnType( |
| 565 | // convert primitive anyopaque to actual void (only for return type) | 880 | // convert primitive anyopaque to actual void (only for return type) |
| 566 | break :blk ZigTag.void_type.init(); | 881 | break :blk ZigTag.void_type.init(); |
| 567 | } else { | 882 | } else { |
| 568 | break :blk transType(c, scope, return_ty, source_loc) catch |err| switch (err) { | 883 | break :blk transType(c, scope, return_ty, .standard, source_loc) catch |err| switch (err) { |
| 569 | error.UnsupportedType => { | 884 | error.UnsupportedType => { |
| 570 | try warn(c, scope, source_loc, "unsupported function proto return type", .{}); | 885 | try warn(c, scope, source_loc, "unsupported function proto return type", .{}); |
| 571 | return err; | 886 | return err; |
| ... | @@ -642,7 +957,7 @@ fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!Z | ... | @@ -642,7 +957,7 @@ fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!Z |
| 642 | // TODO handle other values | 957 | // TODO handle other values |
| 643 | const int = try transCreateNodeAPInt(c, val); | 958 | const int = try transCreateNodeAPInt(c, val); |
| 644 | const as_node = try ZigTag.as.create(c.arena, .{ | 959 | const as_node = try ZigTag.as.create(c.arena, .{ |
| 645 | .lhs = try transType(c, undefined, ty, undefined), | 960 | .lhs = try transType(c, undefined, ty, .standard, undefined), |
| 646 | .rhs = int, | 961 | .rhs = int, |
| 647 | }); | 962 | }); |
| 648 | return maybeSuppressResult(c, result_used, as_node); | 963 | return maybeSuppressResult(c, result_used, as_node); |
test/cases/translate_c/circular_struct_definitions.c created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | struct Bar; | ||
| 2 | |||
| 3 | struct Foo { | ||
| 4 | struct Bar *next; | ||
| 5 | }; | ||
| 6 | |||
| 7 | struct Bar { | ||
| 8 | struct Foo *next; | ||
| 9 | }; | ||
| 10 | |||
| 11 | // translate-c | ||
| 12 | // c_frontend=clang | ||
| 13 | // | ||
| 14 | // pub const struct_Bar = extern struct { | ||
| 15 | // next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo), | ||
| 16 | // }; | ||
| 17 | // | ||
| 18 | // pub const struct_Foo = extern struct { | ||
| 19 | // next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar), | ||
| 20 | // }; | ||
test/cases/translate_c/double_define_struct.c created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | typedef struct Bar Bar; | ||
| 2 | typedef struct Foo Foo; | ||
| 3 | |||
| 4 | struct Foo { | ||
| 5 | Foo *a; | ||
| 6 | }; | ||
| 7 | |||
| 8 | struct Bar { | ||
| 9 | Foo *a; | ||
| 10 | }; | ||
| 11 | |||
| 12 | // translate-c | ||
| 13 | // c_frontend=clang | ||
| 14 | // | ||
| 15 | // pub const struct_Foo = extern struct { | ||
| 16 | // a: [*c]Foo = @import("std").mem.zeroes([*c]Foo), | ||
| 17 | // }; | ||
| 18 | // | ||
| 19 | // pub const Foo = struct_Foo; | ||
| 20 | // | ||
| 21 | // pub const struct_Bar = extern struct { | ||
| 22 | // a: [*c]Foo = @import("std").mem.zeroes([*c]Foo), | ||
| 23 | // }; | ||
| 24 | // | ||
| 25 | // pub const Bar = struct_Bar; | ||
test/cases/translate_c/field_access_is_grouped_if_necessary.c created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | unsigned long foo(unsigned long x) { | ||
| 2 | return ((union{unsigned long _x}){x})._x; | ||
| 3 | } | ||
| 4 | |||
| 5 | // translate-c | ||
| 6 | // c_frontend=clang | ||
| 7 | // | ||
| 8 | // pub export fn foo(arg_x: c_ulong) c_ulong { | ||
| 9 | // var x = arg_x; | ||
| 10 | // _ = &x; | ||
| 11 | // const union_unnamed_1 = extern union { | ||
| 12 | // _x: c_ulong, | ||
| 13 | // }; | ||
| 14 | // _ = &union_unnamed_1; | ||
| 15 | // return (union_unnamed_1{ | ||
| 16 | // ._x = x, | ||
| 17 | // })._x; | ||
| 18 | // } | ||
test/cases/translate_c/global_struct_whose_default_name_conflicts_with_global_is_mangled.c created+15| ... | @@ -0,0 +1,15 @@ | ||
| 1 | struct foo { | ||
| 2 | int x; | ||
| 3 | }; | ||
| 4 | const char *struct_foo = "hello world"; | ||
| 5 | |||
| 6 | // translate-c | ||
| 7 | // c_frontend=clang | ||
| 8 | // | ||
| 9 | // pub const struct_foo_1 = extern struct { | ||
| 10 | // x: c_int = @import("std").mem.zeroes(c_int), | ||
| 11 | // }; | ||
| 12 | // | ||
| 13 | // pub const foo = struct_foo_1; | ||
| 14 | // | ||
| 15 | // pub export var struct_foo: [*c]const u8 = "hello world"; | ||
test/cases/translate_c/large_packed_struct.c created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | struct __attribute__((packed)) bar { | ||
| 2 | short a; | ||
| 3 | float b; | ||
| 4 | double c; | ||
| 5 | short x; | ||
| 6 | float y; | ||
| 7 | double z; | ||
| 8 | }; | ||
| 9 | |||
| 10 | // translate-c | ||
| 11 | // c_frontend=aro,clang | ||
| 12 | // | ||
| 13 | // pub const struct_bar = extern struct { | ||
| 14 | // a: c_short align(1) = @import("std").mem.zeroes(c_short), | ||
| 15 | // b: f32 align(1) = @import("std").mem.zeroes(f32), | ||
| 16 | // c: f64 align(1) = @import("std").mem.zeroes(f64), | ||
| 17 | // x: c_short align(1) = @import("std").mem.zeroes(c_short), | ||
| 18 | // y: f32 align(1) = @import("std").mem.zeroes(f32), | ||
| 19 | // z: f64 align(1) = @import("std").mem.zeroes(f64), | ||
| 20 | // }; | ||
test/cases/translate_c/packed_union_nested_unpacked.c created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | // NOTE: The nested struct is *not* packed/aligned, | ||
| 2 | // even though the parent struct is | ||
| 3 | // this is consistent with GCC docs | ||
| 4 | union Foo{ | ||
| 5 | short x; | ||
| 6 | double y; | ||
| 7 | struct { | ||
| 8 | int b; | ||
| 9 | } z; | ||
| 10 | } __attribute__((packed)); | ||
| 11 | |||
| 12 | // translate-c | ||
| 13 | // c_frontend=aro,clang | ||
| 14 | // | ||
| 15 | // const struct_unnamed_1 = extern struct { | ||
| 16 | // b: c_int = @import("std").mem.zeroes(c_int), | ||
| 17 | // }; | ||
| 18 | // | ||
| 19 | // pub const union_Foo = extern union { | ||
| 20 | // x: c_short align(1), | ||
| 21 | // y: f64 align(1), | ||
| 22 | // z: struct_unnamed_1 align(1), | ||
| 23 | // }; | ||
| 24 | // | ||
| 25 | // pub const Foo = union_Foo; | ||
test/cases/translate_c/packed_union_simple.c created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | union Foo { | ||
| 2 | short x; | ||
| 3 | double y; | ||
| 4 | } __attribute__((packed)); | ||
| 5 | |||
| 6 | // translate-c | ||
| 7 | // c_frontend=aro,clang | ||
| 8 | // | ||
| 9 | // pub const union_Foo = extern union { | ||
| 10 | // x: c_short align(1), | ||
| 11 | // y: f64 align(1), | ||
| 12 | // }; | ||
| 13 | // | ||
| 14 | // pub const Foo = union_Foo; | ||
test/cases/translate_c/pointer_to_struct_demoted_opaque_due_to_bit_fields.c created+15| ... | @@ -0,0 +1,15 @@ | ||
| 1 | struct Foo { | ||
| 2 | unsigned int: 1; | ||
| 3 | }; | ||
| 4 | struct Bar { | ||
| 5 | struct Foo *foo; | ||
| 6 | }; | ||
| 7 | |||
| 8 | // translate-c | ||
| 9 | // c_frontend=clang | ||
| 10 | // | ||
| 11 | // pub const struct_Foo = opaque {}; | ||
| 12 | // | ||
| 13 | // pub const struct_Bar = extern struct { | ||
| 14 | // foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo), | ||
| 15 | // }; | ||
test/cases/translate_c/qualified_struct_and_enum.c created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | struct Foo { | ||
| 2 | int x; | ||
| 3 | int y; | ||
| 4 | }; | ||
| 5 | enum Bar { | ||
| 6 | BarA, | ||
| 7 | BarB, | ||
| 8 | }; | ||
| 9 | void func(struct Foo *a, enum Bar **b); | ||
| 10 | |||
| 11 | // translate-c | ||
| 12 | // c_frontend=clang | ||
| 13 | // target=x86_64-linux,x86_64-macos | ||
| 14 | // | ||
| 15 | // pub const struct_Foo = extern struct { | ||
| 16 | // x: c_int = @import("std").mem.zeroes(c_int), | ||
| 17 | // y: c_int = @import("std").mem.zeroes(c_int), | ||
| 18 | // }; | ||
| 19 | // pub const BarA: c_int = 0; | ||
| 20 | // pub const BarB: c_int = 1; | ||
| 21 | // pub const enum_Bar = c_uint; | ||
| 22 | // pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; | ||
| 23 | // | ||
| 24 | // pub const Foo = struct_Foo; | ||
| 25 | // pub const Bar = enum_Bar; | ||
test/cases/translate_c/qualified_struct_and_enum_msvc.c created+25| ... | @@ -0,0 +1,25 @@ | ||
| 1 | struct Foo { | ||
| 2 | int x; | ||
| 3 | int y; | ||
| 4 | }; | ||
| 5 | enum Bar { | ||
| 6 | BarA, | ||
| 7 | BarB, | ||
| 8 | }; | ||
| 9 | void func(struct Foo *a, enum Bar **b); | ||
| 10 | |||
| 11 | // translate-c | ||
| 12 | // c_frontend=clang | ||
| 13 | // target=x86_64-windows-msvc | ||
| 14 | // | ||
| 15 | // pub const struct_Foo = extern struct { | ||
| 16 | // x: c_int = @import("std").mem.zeroes(c_int), | ||
| 17 | // y: c_int = @import("std").mem.zeroes(c_int), | ||
| 18 | // }; | ||
| 19 | // pub const BarA: c_int = 0; | ||
| 20 | // pub const BarB: c_int = 1; | ||
| 21 | // pub const enum_Bar = c_int; | ||
| 22 | // pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; | ||
| 23 | // | ||
| 24 | // pub const Foo = struct_Foo; | ||
| 25 | // pub const Bar = enum_Bar; | ||
test/cases/translate_c/scoped_record.c created+49| ... | @@ -0,0 +1,49 @@ | ||
| 1 | void foo() { | ||
| 2 | 	struct Foo { | ||
| 3 | 		int A; | ||
| 4 | 		int B; | ||
| 5 | 		int C; | ||
| 6 | 	}; | ||
| 7 | 	struct Foo a = {0}; | ||
| 8 | 	{ | ||
| 9 | 		struct Foo { | ||
| 10 | 			int A; | ||
| 11 | 			int B; | ||
| 12 | 			int C; | ||
| 13 | 		}; | ||
| 14 | 		struct Foo a = {0}; | ||
| 15 | 	} | ||
| 16 | } | ||
| 17 | |||
| 18 | // translate-c | ||
| 19 | // c_frontend=clang | ||
| 20 | // | ||
| 21 | // pub export fn foo() void { | ||
| 22 | // const struct_Foo = extern struct { | ||
| 23 | // A: c_int = @import("std").mem.zeroes(c_int), | ||
| 24 | // B: c_int = @import("std").mem.zeroes(c_int), | ||
| 25 | // C: c_int = @import("std").mem.zeroes(c_int), | ||
| 26 | // }; | ||
| 27 | // _ = &struct_Foo; | ||
| 28 | // var a: struct_Foo = struct_Foo{ | ||
| 29 | // .A = @as(c_int, 0), | ||
| 30 | // .B = 0, | ||
| 31 | // .C = 0, | ||
| 32 | // }; | ||
| 33 | // _ = &a; | ||
| 34 | // { | ||
| 35 | // const struct_Foo_1 = extern struct { | ||
| 36 | // A: c_int = @import("std").mem.zeroes(c_int), | ||
| 37 | // B: c_int = @import("std").mem.zeroes(c_int), | ||
| 38 | // C: c_int = @import("std").mem.zeroes(c_int), | ||
| 39 | // }; | ||
| 40 | // _ = &struct_Foo_1; | ||
| 41 | // var a_2: struct_Foo_1 = struct_Foo_1{ | ||
| 42 | // .A = @as(c_int, 0), | ||
| 43 | // .B = 0, | ||
| 44 | // .C = 0, | ||
| 45 | // }; | ||
| 46 | // _ = &a_2; | ||
| 47 | // } | ||
| 48 | // } | ||
| 49 | |||
test/cases/translate_c/simple_struct.c created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | struct Foo { | ||
| 2 | int x; | ||
| 3 | }; | ||
| 4 | |||
| 5 | // translate-c | ||
| 6 | // c_frontend=aro,clang | ||
| 7 | // | ||
| 8 | // const struct_Foo = extern struct { | ||
| 9 | // x: c_int = @import("std").mem.zeroes(c_int), | ||
| 10 | // }; | ||
| 11 | // | ||
| 12 | // pub const Foo = struct_Foo; | ||
test/cases/translate_c/simple_union.c created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | union Foo { | ||
| 2 | int x; | ||
| 3 | }; | ||
| 4 | |||
| 5 | // translate-c | ||
| 6 | // c_frontend=aro,clang | ||
| 7 | // | ||
| 8 | // pub const union_Foo = extern union { | ||
| 9 | // x: c_int, | ||
| 10 | // }; | ||
| 11 | // | ||
| 12 | // pub const Foo = union_Foo; | ||
test/cases/translate_c/struct_in_struct_init_to_zero.c created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | struct Foo { | ||
| 2 | int a; | ||
| 3 | struct Bar { | ||
| 4 | int a; | ||
| 5 | } b; | ||
| 6 | } a = {}; | ||
| 7 | #define PTR void * | ||
| 8 | |||
| 9 | // translate-c | ||
| 10 | // c_frontend=clang | ||
| 11 | // | ||
| 12 | // pub const struct_Bar_1 = extern struct { | ||
| 13 | // a: c_int = @import("std").mem.zeroes(c_int), | ||
| 14 | // }; | ||
| 15 | // pub const struct_Foo = extern struct { | ||
| 16 | // a: c_int = @import("std").mem.zeroes(c_int), | ||
| 17 | // b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1), | ||
| 18 | // }; | ||
| 19 | // pub export var a: struct_Foo = struct_Foo{ | ||
| 20 | // .a = 0, | ||
| 21 | // .b = @import("std").mem.zeroes(struct_Bar_1), | ||
| 22 | // }; | ||
| 23 | // | ||
| 24 | // pub const PTR = ?*anyopaque; | ||
test/cases/translate_c/struct_with_aligned_fields.c created+10| ... | @@ -0,0 +1,10 @@ | ||
| 1 | struct foo { | ||
| 2 | __attribute__((aligned(4))) short bar; | ||
| 3 | }; | ||
| 4 | |||
| 5 | // translate-c | ||
| 6 | // c_frontend=aro,clang | ||
| 7 | // | ||
| 8 | // pub const struct_foo = extern struct { | ||
| 9 | // bar: c_short align(4) = @import("std").mem.zeroes(c_short), | ||
| 10 | // }; | ||
test/cases/translate_c/struct_with_invalid_field_alignment.c created+35| ... | @@ -0,0 +1,35 @@ | ||
| 1 | // The aligned attribute cannot decrease the alignment of a field. The packed attribute is required | ||
| 2 | // for decreasing the alignment. gcc and clang will compile these structs without error | ||
| 3 | // (and possibly without warning), but checking the alignment will reveal a different value than | ||
| 4 | // what was requested. This is consistent with the gcc documentation on type attributes. | ||
| 5 | // | ||
| 6 | // This test is currently broken for the clang frontend. See issue #19307. | ||
| 7 | |||
| 8 | struct foo { | ||
| 9 | __attribute__((aligned(1)))int x; | ||
| 10 | }; | ||
| 11 | |||
| 12 | struct bar { | ||
| 13 | __attribute__((aligned(2)))float y; | ||
| 14 | }; | ||
| 15 | |||
| 16 | struct baz { | ||
| 17 | __attribute__((aligned(4)))double z; | ||
| 18 | }; | ||
| 19 | |||
| 20 | // translate-c | ||
| 21 | // c_frontend=aro | ||
| 22 | // target=x86_64-linux | ||
| 23 | // | ||
| 24 | // pub const struct_foo = extern struct { | ||
| 25 | // x: c_int = @import("std").mem.zeroes(c_int), | ||
| 26 | // }; | ||
| 27 | // | ||
| 28 | // pub const struct_bar = extern struct { | ||
| 29 | // y: f32 = @import("std").mem.zeroes(f32), | ||
| 30 | // }; | ||
| 31 | // | ||
| 32 | // pub const struct_baz = extern struct { | ||
| 33 | // z: f64 = @import("std").mem.zeroes(f64), | ||
| 34 | // }; | ||
| 35 | // | ||
test/cases/translate_c/type_referenced_struct.c created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | // When clang uses the <arch>-windows-none, triple it behaves as MSVC and | ||
| 2 | // interprets the inner `struct Bar` as an anonymous structure | ||
| 3 | struct Foo { | ||
| 4 | struct Bar{ | ||
| 5 | int b; | ||
| 6 | }; | ||
| 7 | struct Bar c; | ||
| 8 | }; | ||
| 9 | |||
| 10 | // translate-c | ||
| 11 | // c_frontend=aro,clang | ||
| 12 | // target=x86_64-linux-gnu | ||
| 13 | // | ||
| 14 | // pub const struct_Bar_1 = extern struct { | ||
| 15 | // b: c_int = @import("std").mem.zeroes(c_int), | ||
| 16 | // }; | ||
| 17 | // pub const struct_Foo = extern struct { | ||
| 18 | // c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1), | ||
| 19 | // }; | ||
test/cases/translate_c/union_initializer.c created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | union { int x; char c[4]; } | ||
| 2 | ua = {1}, | ||
| 3 | ub = {.c={'a','b','b','a'}}; | ||
| 4 | |||
| 5 | // translate-c | ||
| 6 | // c_frontend=clang | ||
| 7 | // | ||
| 8 | // const union_unnamed_1 = extern union { | ||
| 9 | // x: c_int, | ||
| 10 | // c: [4]u8, | ||
| 11 | // }; | ||
| 12 | // pub export var ua: union_unnamed_1 = union_unnamed_1{ | ||
| 13 | // .x = @as(c_int, 1), | ||
| 14 | // }; | ||
| 15 | // pub export var ub: union_unnamed_1 = union_unnamed_1{ | ||
| 16 | // .c = [4]u8{ | ||
| 17 | // 'a', | ||
| 18 | // 'b', | ||
| 19 | // 'b', | ||
| 20 | // 'a', | ||
| 21 | // }, | ||
| 22 | // }; | ||
test/cases/translate_c/union_struct_forward_decl.c created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | struct A; | ||
| 2 | union B; | ||
| 3 | enum C; | ||
| 4 | |||
| 5 | struct A { | ||
| 6 | short x; | ||
| 7 | double y; | ||
| 8 | }; | ||
| 9 | |||
| 10 | union B { | ||
| 11 | short x; | ||
| 12 | double y; | ||
| 13 | }; | ||
| 14 | |||
| 15 | struct Foo { | ||
| 16 | struct A a; | ||
| 17 | union B b; | ||
| 18 | }; | ||
| 19 | |||
| 20 | |||
| 21 | // translate-c | ||
| 22 | // c_frontend=aro,clang | ||
| 23 | // | ||
| 24 | // pub const struct_A = extern struct { | ||
| 25 | // x: c_short = @import("std").mem.zeroes(c_short), | ||
| 26 | // y: f64 = @import("std").mem.zeroes(f64), | ||
| 27 | // }; | ||
| 28 | // | ||
| 29 | // pub const union_B = extern union { | ||
| 30 | // x: c_short, | ||
| 31 | // y: f64, | ||
| 32 | // }; | ||
| 33 | // | ||
| 34 | // pub const struct_Foo = extern struct { | ||
| 35 | // a: struct_A = @import("std").mem.zeroes(struct_A), | ||
| 36 | // b: union_B = @import("std").mem.zeroes(union_B), | ||
| 37 | // }; | ||
test/cases/translate_c/unnamed_fields_have_predictable_names.c created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | struct a { | ||
| 2 | struct { int x; }; | ||
| 3 | }; | ||
| 4 | struct b { | ||
| 5 | struct { int y; }; | ||
| 6 | }; | ||
| 7 | |||
| 8 | // translate-c | ||
| 9 | // c_frontend=aro,clang | ||
| 10 | // | ||
| 11 | // const struct_unnamed_1 = extern struct { | ||
| 12 | // x: c_int = @import("std").mem.zeroes(c_int), | ||
| 13 | // }; | ||
| 14 | // pub const struct_a = extern struct { | ||
| 15 | // unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1), | ||
| 16 | // }; | ||
| 17 | // const struct_unnamed_2 = extern struct { | ||
| 18 | // y: c_int = @import("std").mem.zeroes(c_int), | ||
| 19 | // }; | ||
| 20 | // pub const struct_b = extern struct { | ||
| 21 | // unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2), | ||
| 22 | // }; | ||
test/cases/translate_c/zero_width_field_alignment.c created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | struct __attribute__((packed)) foo { | ||
| 2 | int x; | ||
| 3 | struct {}; | ||
| 4 | float y; | ||
| 5 | union {}; | ||
| 6 | }; | ||
| 7 | |||
| 8 | // translate-c | ||
| 9 | // c_frontend=aro | ||
| 10 | // | ||
| 11 | // const struct_unnamed_1 = extern struct {}; | ||
| 12 | // const union_unnamed_2 = extern union {}; | ||
| 13 | // pub const struct_foo = extern struct { | ||
| 14 | // x: c_int align(1) = @import("std").mem.zeroes(c_int), | ||
| 15 | // unnamed_0: struct_unnamed_1 align(1) = @import("std").mem.zeroes(struct_unnamed_1), | ||
| 16 | // y: f32 align(1) = @import("std").mem.zeroes(f32), | ||
| 17 | // unnamed_1: union_unnamed_2 align(1) = @import("std").mem.zeroes(union_unnamed_2), | ||
| 18 | // }; | ||
test/cases/translate_c/zig_keywords_in_c_code.c created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | struct comptime { | ||
| 2 | int defer; | ||
| 3 | }; | ||
| 4 | |||
| 5 | // translate-c | ||
| 6 | // c_frontend=aro,clang | ||
| 7 | // | ||
| 8 | // pub const struct_comptime = extern struct { | ||
| 9 | // @"defer": c_int = @import("std").mem.zeroes(c_int), | ||
| 10 | // }; | ||
| 11 | // | ||
| 12 | // pub const @"comptime" = struct_comptime; | ||
test/translate_c.zig-370| ... | @@ -74,24 +74,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -74,24 +74,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 74 | \\pub extern fn main() c_int; | 74 | \\pub extern fn main() c_int; |
| 75 | }); | 75 | }); |
| 76 | 76 | ||
| 77 | cases.add("field access is grouped if necessary", | ||
| 78 | \\unsigned long foo(unsigned long x) { | ||
| 79 | \\ return ((union{unsigned long _x}){x})._x; | ||
| 80 | \\} | ||
| 81 | , &[_][]const u8{ | ||
| 82 | \\pub export fn foo(arg_x: c_ulong) c_ulong { | ||
| 83 | \\ var x = arg_x; | ||
| 84 | \\ _ = &x; | ||
| 85 | \\ const union_unnamed_1 = extern union { | ||
| 86 | \\ _x: c_ulong, | ||
| 87 | \\ }; | ||
| 88 | \\ _ = &union_unnamed_1; | ||
| 89 | \\ return (union_unnamed_1{ | ||
| 90 | \\ ._x = x, | ||
| 91 | \\ })._x; | ||
| 92 | \\} | ||
| 93 | }); | ||
| 94 | |||
| 95 | cases.add("unnamed child types of typedef receive typedef's name", | 77 | cases.add("unnamed child types of typedef receive typedef's name", |
| 96 | \\typedef enum { | 78 | \\typedef enum { |
| 97 | \\ FooA, | 79 | \\ FooA, |
| ... | @@ -149,78 +131,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -149,78 +131,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 149 | \\} | 131 | \\} |
| 150 | }); | 132 | }); |
| 151 | 133 | ||
| 152 | cases.add("struct in struct init to zero", | ||
| 153 | \\struct Foo { | ||
| 154 | \\ int a; | ||
| 155 | \\ struct Bar { | ||
| 156 | \\ int a; | ||
| 157 | \\ } b; | ||
| 158 | \\} a = {}; | ||
| 159 | \\#define PTR void * | ||
| 160 | , &[_][]const u8{ | ||
| 161 | \\pub const struct_Bar_1 = extern struct { | ||
| 162 | \\ a: c_int = @import("std").mem.zeroes(c_int), | ||
| 163 | \\}; | ||
| 164 | \\pub const struct_Foo = extern struct { | ||
| 165 | \\ a: c_int = @import("std").mem.zeroes(c_int), | ||
| 166 | \\ b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1), | ||
| 167 | \\}; | ||
| 168 | \\pub export var a: struct_Foo = struct_Foo{ | ||
| 169 | \\ .a = 0, | ||
| 170 | \\ .b = @import("std").mem.zeroes(struct_Bar_1), | ||
| 171 | \\}; | ||
| 172 | , | ||
| 173 | \\pub const PTR = ?*anyopaque; | ||
| 174 | }); | ||
| 175 | |||
| 176 | cases.add("scoped record", | ||
| 177 | \\void foo() { | ||
| 178 | \\	struct Foo { | ||
| 179 | \\		int A; | ||
| 180 | \\		int B; | ||
| 181 | \\		int C; | ||
| 182 | \\	}; | ||
| 183 | \\	struct Foo a = {0}; | ||
| 184 | \\	{ | ||
| 185 | \\		struct Foo { | ||
| 186 | \\			int A; | ||
| 187 | \\			int B; | ||
| 188 | \\			int C; | ||
| 189 | \\		}; | ||
| 190 | \\		struct Foo a = {0}; | ||
| 191 | \\	} | ||
| 192 | \\} | ||
| 193 | , &[_][]const u8{ | ||
| 194 | \\pub export fn foo() void { | ||
| 195 | \\ const struct_Foo = extern struct { | ||
| 196 | \\ A: c_int = @import("std").mem.zeroes(c_int), | ||
| 197 | \\ B: c_int = @import("std").mem.zeroes(c_int), | ||
| 198 | \\ C: c_int = @import("std").mem.zeroes(c_int), | ||
| 199 | \\ }; | ||
| 200 | \\ _ = &struct_Foo; | ||
| 201 | \\ var a: struct_Foo = struct_Foo{ | ||
| 202 | \\ .A = @as(c_int, 0), | ||
| 203 | \\ .B = 0, | ||
| 204 | \\ .C = 0, | ||
| 205 | \\ }; | ||
| 206 | \\ _ = &a; | ||
| 207 | \\ { | ||
| 208 | \\ const struct_Foo_1 = extern struct { | ||
| 209 | \\ A: c_int = @import("std").mem.zeroes(c_int), | ||
| 210 | \\ B: c_int = @import("std").mem.zeroes(c_int), | ||
| 211 | \\ C: c_int = @import("std").mem.zeroes(c_int), | ||
| 212 | \\ }; | ||
| 213 | \\ _ = &struct_Foo_1; | ||
| 214 | \\ var a_2: struct_Foo_1 = struct_Foo_1{ | ||
| 215 | \\ .A = @as(c_int, 0), | ||
| 216 | \\ .B = 0, | ||
| 217 | \\ .C = 0, | ||
| 218 | \\ }; | ||
| 219 | \\ _ = &a_2; | ||
| 220 | \\ } | ||
| 221 | \\} | ||
| 222 | }); | ||
| 223 | |||
| 224 | cases.add("scoped typedef", | 134 | cases.add("scoped typedef", |
| 225 | \\void foo() { | 135 | \\void foo() { |
| 226 | \\	typedef union { | 136 | \\	typedef union { |
| ... | @@ -466,16 +376,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -466,16 +376,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 466 | \\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4)); | 376 | \\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4)); |
| 467 | }); | 377 | }); |
| 468 | 378 | ||
| 469 | cases.add("struct with aligned fields", | ||
| 470 | \\struct foo { | ||
| 471 | \\ __attribute__((aligned(1))) short bar; | ||
| 472 | \\}; | ||
| 473 | , &[_][]const u8{ | ||
| 474 | \\pub const struct_foo = extern struct { | ||
| 475 | \\ bar: c_short align(1) = @import("std").mem.zeroes(c_short), | ||
| 476 | \\}; | ||
| 477 | }); | ||
| 478 | |||
| 479 | cases.add("struct with flexible array", | 379 | cases.add("struct with flexible array", |
| 480 | \\struct foo { int x; int y[]; }; | 380 | \\struct foo { int x; int y[]; }; |
| 481 | \\struct bar { int x; int y[0]; }; | 381 | \\struct bar { int x; int y[0]; }; |
| ... | @@ -661,28 +561,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -661,28 +561,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 661 | \\} | 561 | \\} |
| 662 | }); | 562 | }); |
| 663 | 563 | ||
| 664 | cases.add("union initializer", | ||
| 665 | \\union { int x; char c[4]; } | ||
| 666 | \\ ua = {1}, | ||
| 667 | \\ ub = {.c={'a','b','b','a'}}; | ||
| 668 | , &[_][]const u8{ | ||
| 669 | \\const union_unnamed_1 = extern union { | ||
| 670 | \\ x: c_int, | ||
| 671 | \\ c: [4]u8, | ||
| 672 | \\}; | ||
| 673 | \\pub export var ua: union_unnamed_1 = union_unnamed_1{ | ||
| 674 | \\ .x = @as(c_int, 1), | ||
| 675 | \\}; | ||
| 676 | \\pub export var ub: union_unnamed_1 = union_unnamed_1{ | ||
| 677 | \\ .c = [4]u8{ | ||
| 678 | \\ 'a', | ||
| 679 | \\ 'b', | ||
| 680 | \\ 'b', | ||
| 681 | \\ 'a', | ||
| 682 | \\ }, | ||
| 683 | \\}; | ||
| 684 | }); | ||
| 685 | |||
| 686 | cases.add("struct initializer - simple", | 564 | cases.add("struct initializer - simple", |
| 687 | \\typedef struct { int x; } foo; | 565 | \\typedef struct { int x; } foo; |
| 688 | \\struct {double x,y,z;} s0 = {1.2, 1.3}; | 566 | \\struct {double x,y,z;} s0 = {1.2, 1.3}; |
| ... | @@ -992,21 +870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -992,21 +870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 992 | \\}; | 870 | \\}; |
| 993 | }); | 871 | }); |
| 994 | 872 | ||
| 995 | cases.add("pointer to struct demoted to opaque due to bit fields", | ||
| 996 | \\struct Foo { | ||
| 997 | \\ unsigned int: 1; | ||
| 998 | \\}; | ||
| 999 | \\struct Bar { | ||
| 1000 | \\ struct Foo *foo; | ||
| 1001 | \\}; | ||
| 1002 | , &[_][]const u8{ | ||
| 1003 | \\pub const struct_Foo = opaque {}; | ||
| 1004 | , | ||
| 1005 | \\pub const struct_Bar = extern struct { | ||
| 1006 | \\ foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo), | ||
| 1007 | \\}; | ||
| 1008 | }); | ||
| 1009 | |||
| 1010 | cases.add("macro with left shift", | 873 | cases.add("macro with left shift", |
| 1011 | \\#define REDISMODULE_READ (1<<0) | 874 | \\#define REDISMODULE_READ (1<<0) |
| 1012 | , &[_][]const u8{ | 875 | , &[_][]const u8{ |
| ... | @@ -1022,45 +885,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1022,45 +885,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1022 | \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1); | 885 | \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1); |
| 1023 | }); | 886 | }); |
| 1024 | 887 | ||
| 1025 | cases.add("double define struct", | ||
| 1026 | \\typedef struct Bar Bar; | ||
| 1027 | \\typedef struct Foo Foo; | ||
| 1028 | \\ | ||
| 1029 | \\struct Foo { | ||
| 1030 | \\ Foo *a; | ||
| 1031 | \\}; | ||
| 1032 | \\ | ||
| 1033 | \\struct Bar { | ||
| 1034 | \\ Foo *a; | ||
| 1035 | \\}; | ||
| 1036 | , &[_][]const u8{ | ||
| 1037 | \\pub const struct_Foo = extern struct { | ||
| 1038 | \\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo), | ||
| 1039 | \\}; | ||
| 1040 | , | ||
| 1041 | \\pub const Foo = struct_Foo; | ||
| 1042 | , | ||
| 1043 | \\pub const struct_Bar = extern struct { | ||
| 1044 | \\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo), | ||
| 1045 | \\}; | ||
| 1046 | , | ||
| 1047 | \\pub const Bar = struct_Bar; | ||
| 1048 | }); | ||
| 1049 | |||
| 1050 | cases.add("simple struct", | ||
| 1051 | \\struct Foo { | ||
| 1052 | \\ int x; | ||
| 1053 | \\ char *y; | ||
| 1054 | \\}; | ||
| 1055 | , &[_][]const u8{ | ||
| 1056 | \\const struct_Foo = extern struct { | ||
| 1057 | \\ x: c_int = @import("std").mem.zeroes(c_int), | ||
| 1058 | \\ y: [*c]u8 = @import("std").mem.zeroes([*c]u8), | ||
| 1059 | \\}; | ||
| 1060 | , | ||
| 1061 | \\pub const Foo = struct_Foo; | ||
| 1062 | }); | ||
| 1063 | |||
| 1064 | cases.add("self referential struct with function pointer", | 888 | cases.add("self referential struct with function pointer", |
| 1065 | \\struct Foo { | 889 | \\struct Foo { |
| 1066 | \\ void (*derp)(struct Foo *foo); | 890 | \\ void (*derp)(struct Foo *foo); |
| ... | @@ -1099,44 +923,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1099,44 +923,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1099 | \\pub const THING2 = THING1; | 923 | \\pub const THING2 = THING1; |
| 1100 | }); | 924 | }); |
| 1101 | 925 | ||
| 1102 | cases.add("circular struct definitions", | ||
| 1103 | \\struct Bar; | ||
| 1104 | \\ | ||
| 1105 | \\struct Foo { | ||
| 1106 | \\ struct Bar *next; | ||
| 1107 | \\}; | ||
| 1108 | \\ | ||
| 1109 | \\struct Bar { | ||
| 1110 | \\ struct Foo *next; | ||
| 1111 | \\}; | ||
| 1112 | , &[_][]const u8{ | ||
| 1113 | \\pub const struct_Bar = extern struct { | ||
| 1114 | \\ next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo), | ||
| 1115 | \\}; | ||
| 1116 | , | ||
| 1117 | \\pub const struct_Foo = extern struct { | ||
| 1118 | \\ next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar), | ||
| 1119 | \\}; | ||
| 1120 | }); | ||
| 1121 | |||
| 1122 | cases.add("#define string", | 926 | cases.add("#define string", |
| 1123 | \\#define foo "a string" | 927 | \\#define foo "a string" |
| 1124 | , &[_][]const u8{ | 928 | , &[_][]const u8{ |
| 1125 | \\pub const foo = "a string"; | 929 | \\pub const foo = "a string"; |
| 1126 | }); | 930 | }); |
| 1127 | 931 | ||
| 1128 | cases.add("zig keywords in C code", | ||
| 1129 | \\struct comptime { | ||
| 1130 | \\ int defer; | ||
| 1131 | \\}; | ||
| 1132 | , &[_][]const u8{ | ||
| 1133 | \\pub const struct_comptime = extern struct { | ||
| 1134 | \\ @"defer": c_int = @import("std").mem.zeroes(c_int), | ||
| 1135 | \\}; | ||
| 1136 | , | ||
| 1137 | \\pub const @"comptime" = struct_comptime; | ||
| 1138 | }); | ||
| 1139 | |||
| 1140 | cases.add("macro with parens around negative number", | 932 | cases.add("macro with parens around negative number", |
| 1141 | \\#define LUA_GLOBALSINDEX (-10002) | 933 | \\#define LUA_GLOBALSINDEX (-10002) |
| 1142 | , &[_][]const u8{ | 934 | , &[_][]const u8{ |
| ... | @@ -1393,88 +1185,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1393,88 +1185,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1393 | \\} | 1185 | \\} |
| 1394 | }); | 1186 | }); |
| 1395 | 1187 | ||
| 1396 | cases.add("simple union", | ||
| 1397 | \\union Foo { | ||
| 1398 | \\ int x; | ||
| 1399 | \\ double y; | ||
| 1400 | \\}; | ||
| 1401 | , &[_][]const u8{ | ||
| 1402 | \\pub const union_Foo = extern union { | ||
| 1403 | \\ x: c_int, | ||
| 1404 | \\ y: f64, | ||
| 1405 | \\}; | ||
| 1406 | , | ||
| 1407 | \\pub const Foo = union_Foo; | ||
| 1408 | }); | ||
| 1409 | |||
| 1410 | cases.add("packed union - simple", | ||
| 1411 | \\union Foo { | ||
| 1412 | \\ char x; | ||
| 1413 | \\ double y; | ||
| 1414 | \\} __attribute__((packed)); | ||
| 1415 | , &[_][]const u8{ | ||
| 1416 | \\pub const union_Foo = extern union { | ||
| 1417 | \\ x: u8 align(1), | ||
| 1418 | \\ y: f64 align(1), | ||
| 1419 | \\}; | ||
| 1420 | , | ||
| 1421 | \\pub const Foo = union_Foo; | ||
| 1422 | }); | ||
| 1423 | |||
| 1424 | cases.add("packed union - nested unpacked", | ||
| 1425 | \\union Foo{ | ||
| 1426 | \\ char x; | ||
| 1427 | \\ double y; | ||
| 1428 | \\ struct { | ||
| 1429 | \\ char a; | ||
| 1430 | \\ int b; | ||
| 1431 | \\ } z; | ||
| 1432 | \\} __attribute__((packed)); | ||
| 1433 | , &[_][]const u8{ | ||
| 1434 | // NOTE: The nested struct is *not* packed/aligned, | ||
| 1435 | // even though the parent struct is | ||
| 1436 | // this is consistent with GCC docs | ||
| 1437 | \\const struct_unnamed_1 = extern struct { | ||
| 1438 | \\ a: u8 = @import("std").mem.zeroes(u8), | ||
| 1439 | \\ b: c_int = @import("std").mem.zeroes(c_int), | ||
| 1440 | \\}; | ||
| 1441 | , | ||
| 1442 | \\pub const union_Foo = extern union { | ||
| 1443 | \\ x: u8 align(1), | ||
| 1444 | \\ y: f64 align(1), | ||
| 1445 | \\ z: struct_unnamed_1 align(1), | ||
| 1446 | \\}; | ||
| 1447 | , | ||
| 1448 | \\pub const Foo = union_Foo; | ||
| 1449 | }); | ||
| 1450 | |||
| 1451 | cases.add("packed union - nested packed", | ||
| 1452 | \\union Foo{ | ||
| 1453 | \\ char x; | ||
| 1454 | \\ double y; | ||
| 1455 | \\ struct { | ||
| 1456 | \\ char a; | ||
| 1457 | \\ int b; | ||
| 1458 | \\ } __attribute__((packed)) z; | ||
| 1459 | \\} __attribute__((packed)); | ||
| 1460 | , &[_][]const u8{ | ||
| 1461 | // in order for the nested struct to be packed, it must | ||
| 1462 | // have an independent packed declaration on | ||
| 1463 | // the nested type (see GCC docs for details) | ||
| 1464 | \\const struct_unnamed_1 = extern struct { | ||
| 1465 | \\ a: u8 align(1) = @import("std").mem.zeroes(u8), | ||
| 1466 | \\ b: c_int align(1) = @import("std").mem.zeroes(c_int), | ||
| 1467 | \\}; | ||
| 1468 | , | ||
| 1469 | \\pub const union_Foo = extern union { | ||
| 1470 | \\ x: u8 align(1), | ||
| 1471 | \\ y: f64 align(1), | ||
| 1472 | \\ z: struct_unnamed_1 align(1), | ||
| 1473 | \\}; | ||
| 1474 | , | ||
| 1475 | \\pub const Foo = union_Foo; | ||
| 1476 | }); | ||
| 1477 | |||
| 1478 | cases.add("string literal", | 1188 | cases.add("string literal", |
| 1479 | \\const char *foo(void) { | 1189 | \\const char *foo(void) { |
| 1480 | \\ return "bar"; | 1190 | \\ return "bar"; |
| ... | @@ -2395,27 +2105,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2395,27 +2105,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2395 | \\ } | 2105 | \\ } |
| 2396 | \\} | 2106 | \\} |
| 2397 | }); | 2107 | }); |
| 2398 | |||
| 2399 | if (builtin.os.tag != .windows) { | ||
| 2400 | // When clang uses the <arch>-windows-none triple it behaves as MSVC and | ||
| 2401 | // interprets the inner `struct Bar` as an anonymous structure | ||
| 2402 | cases.add("type referenced struct", | ||
| 2403 | \\struct Foo { | ||
| 2404 | \\ struct Bar{ | ||
| 2405 | \\ int b; | ||
| 2406 | \\ }; | ||
| 2407 | \\ struct Bar c; | ||
| 2408 | \\}; | ||
| 2409 | , &[_][]const u8{ | ||
| 2410 | \\pub const struct_Bar_1 = extern struct { | ||
| 2411 | \\ b: c_int = @import("std").mem.zeroes(c_int), | ||
| 2412 | \\}; | ||
| 2413 | \\pub const struct_Foo = extern struct { | ||
| 2414 | \\ c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1), | ||
| 2415 | \\}; | ||
| 2416 | }); | ||
| 2417 | } | ||
| 2418 | |||
| 2419 | cases.add("undefined array global", | 2108 | cases.add("undefined array global", |
| 2420 | \\int array[100] = {}; | 2109 | \\int array[100] = {}; |
| 2421 | , &[_][]const u8{ | 2110 | , &[_][]const u8{ |
| ... | @@ -2634,32 +2323,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2634,32 +2323,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2634 | \\pub const Foo = enum_Foo; | 2323 | \\pub const Foo = enum_Foo; |
| 2635 | }); | 2324 | }); |
| 2636 | 2325 | ||
| 2637 | cases.add("qualified struct and enum", | ||
| 2638 | \\struct Foo { | ||
| 2639 | \\ int x; | ||
| 2640 | \\ int y; | ||
| 2641 | \\}; | ||
| 2642 | \\enum Bar { | ||
| 2643 | \\ BarA, | ||
| 2644 | \\ BarB, | ||
| 2645 | \\}; | ||
| 2646 | \\void func(struct Foo *a, enum Bar **b); | ||
| 2647 | , &[_][]const u8{ | ||
| 2648 | \\pub const struct_Foo = extern struct { | ||
| 2649 | \\ x: c_int = @import("std").mem.zeroes(c_int), | ||
| 2650 | \\ y: c_int = @import("std").mem.zeroes(c_int), | ||
| 2651 | \\}; | ||
| 2652 | \\pub const BarA: c_int = 0; | ||
| 2653 | \\pub const BarB: c_int = 1; | ||
| 2654 | \\pub const enum_Bar = | ||
| 2655 | ++ " " ++ default_enum_type ++ | ||
| 2656 | \\; | ||
| 2657 | \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; | ||
| 2658 | , | ||
| 2659 | \\pub const Foo = struct_Foo; | ||
| 2660 | \\pub const Bar = enum_Bar; | ||
| 2661 | }); | ||
| 2662 | |||
| 2663 | cases.add("bitwise binary operators, simpler parens", | 2326 | cases.add("bitwise binary operators, simpler parens", |
| 2664 | \\int max(int a, int b) { | 2327 | \\int max(int a, int b) { |
| 2665 | \\ return (a & b) ^ (a | b); | 2328 | \\ return (a & b) ^ (a | b); |
| ... | @@ -3756,24 +3419,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3756,24 +3419,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3756 | }); | 3419 | }); |
| 3757 | } | 3420 | } |
| 3758 | 3421 | ||
| 3759 | cases.add("unnamed fields have predictable names", | ||
| 3760 | \\struct a { | ||
| 3761 | \\ struct {}; | ||
| 3762 | \\}; | ||
| 3763 | \\struct b { | ||
| 3764 | \\ struct {}; | ||
| 3765 | \\}; | ||
| 3766 | , &[_][]const u8{ | ||
| 3767 | \\const struct_unnamed_1 = extern struct {}; | ||
| 3768 | \\pub const struct_a = extern struct { | ||
| 3769 | \\ unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1), | ||
| 3770 | \\}; | ||
| 3771 | \\const struct_unnamed_2 = extern struct {}; | ||
| 3772 | \\pub const struct_b = extern struct { | ||
| 3773 | \\ unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2), | ||
| 3774 | \\}; | ||
| 3775 | }); | ||
| 3776 | |||
| 3777 | cases.add("integer literal promotion", | 3422 | cases.add("integer literal promotion", |
| 3778 | \\#define GUARANTEED_TO_FIT_1 1024 | 3423 | \\#define GUARANTEED_TO_FIT_1 1024 |
| 3779 | \\#define GUARANTEED_TO_FIT_2 10241024L | 3424 | \\#define GUARANTEED_TO_FIT_2 10241024L |
| ... | @@ -4283,21 +3928,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -4283,21 +3928,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 4283 | \\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`"); | 3928 | \\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`"); |
| 4284 | }); | 3929 | }); |
| 4285 | 3930 | ||
| 4286 | cases.add("global struct whose default name conflicts with global is mangled", | ||
| 4287 | \\struct foo { | ||
| 4288 | \\ int x; | ||
| 4289 | \\}; | ||
| 4290 | \\const char *struct_foo = "hello world"; | ||
| 4291 | , &[_][]const u8{ | ||
| 4292 | \\pub const struct_foo_1 = extern struct { | ||
| 4293 | \\ x: c_int = @import("std").mem.zeroes(c_int), | ||
| 4294 | \\}; | ||
| 4295 | , | ||
| 4296 | \\pub const foo = struct_foo_1; | ||
| 4297 | , | ||
| 4298 | \\pub export var struct_foo: [*c]const u8 = "hello world"; | ||
| 4299 | }); | ||
| 4300 | |||
| 4301 | cases.add("unsupport declare statement at the last of a compound statement which belongs to a statement expr", | 3931 | cases.add("unsupport declare statement at the last of a compound statement which belongs to a statement expr", |
| 4302 | \\void somefunc(void) { | 3932 | \\void somefunc(void) { |
| 4303 | \\ int y; | 3933 | \\ int y; |