diff --git a/src/c_tokenizer.cpp b/src/c_tokenizer.cpp index 044831f72e..6be2cf991e 100644 --- a/src/c_tokenizer.cpp +++ b/src/c_tokenizer.cpp @@ -120,6 +120,7 @@ static void begin_token(CTokenize *ctok, CTokId id) { case CTokIdLParen: case CTokIdRParen: case CTokIdEOF: + case CTokIdDot: break; } } @@ -216,9 +217,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) { buf_append_char(&ctok->buf, '0'); break; case '.': - begin_token(ctok, CTokIdNumLitFloat); - ctok->state = CTokStateFloat; - buf_init_from_str(&ctok->buf, "0."); + begin_token(ctok, CTokIdDot); + end_token(ctok); break; case '(': begin_token(ctok, CTokIdLParen); @@ -238,6 +238,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) { break; case CTokStateFloat: switch (*c) { + case '.': + break; case 'e': case 'E': buf_append_char(&ctok->buf, 'e'); diff --git a/src/c_tokenizer.hpp b/src/c_tokenizer.hpp index 8eea6c56c7..a3df2b94af 100644 --- a/src/c_tokenizer.hpp +++ b/src/c_tokenizer.hpp @@ -21,6 +21,7 @@ enum CTokId { CTokIdLParen, CTokIdRParen, CTokIdEOF, + CTokIdDot, }; enum CNumLitSuffix { diff --git a/src/ir.cpp b/src/ir.cpp index f632a261f6..7c15b48bee 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6302,8 +6302,9 @@ static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char buf_appendf(name, ")"); return name; } else { + //Note: C-imports do not have valid location information return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name, - buf_ptr(source_node->owner->path), source_node->line + 1, source_node->column + 1); + (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1); } } } diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 72ac7b3697..d50cc1d315 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -23,11 +23,6 @@ using namespace clang; -struct MacroSymbol { - Buf *name; - Buf *value; -}; - struct Alias { Buf *new_name; Buf *canon_name; @@ -44,7 +39,6 @@ struct Context { HashMap global_table; SourceManager *source_manager; ZigList aliases; - ZigList macro_symbols; AstNode *source_node; bool warnings_on; @@ -351,8 +345,7 @@ static AstNode *trans_create_node_var_decl_local(Context *c, bool is_const, Buf return trans_create_node_var_decl(c, VisibModPrivate, is_const, var_name, type_node, init_node); } - -static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, Buf *var_name, AstNode *src_proto_node) { +static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *ref_node, AstNode *src_proto_node) { AstNode *fn_def = trans_create_node(c, NodeTypeFnDef); AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto); fn_proto->data.fn_proto.visib_mod = c->visib_mod; @@ -363,7 +356,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, Buf *var_n fn_def->data.fn_def.fn_proto = fn_proto; fn_proto->data.fn_proto.fn_def_node = fn_def; - AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, trans_create_node_symbol(c, var_name)); + AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node); AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr); fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node; @@ -3517,7 +3510,6 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { } const char *raw_name = decl_name(record_decl); - const char *container_kind_name; ContainerKind container_kind; if (record_decl->isUnion()) { @@ -3809,6 +3801,83 @@ static void render_aliases(Context *c) { } } +static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node); + +static AstNode *trans_lookup_ast_container(Context *c, AstNode *type_node) { + if (type_node == nullptr) { + return nullptr; + } else if (type_node->type == NodeTypeContainerDecl) { + return type_node; + } else if (type_node->type == NodeTypePrefixOpExpr) { + return type_node; + } else if (type_node->type == NodeTypeSymbol) { + AstNode *existing_node = get_global(c, type_node->data.symbol_expr.symbol); + if (existing_node == nullptr) + return nullptr; + if (existing_node->type != NodeTypeVariableDeclaration) + return nullptr; + return trans_lookup_ast_container(c, existing_node->data.variable_declaration.expr); + } else if (type_node->type == NodeTypeFieldAccessExpr) { + AstNode *container_node = trans_lookup_ast_container_typeof(c, type_node->data.field_access_expr.struct_expr); + if (container_node == nullptr) + return nullptr; + if (container_node->type != NodeTypeContainerDecl) + return container_node; + + for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) { + AstNode *field_node = container_node->data.container_decl.fields.items[i]; + if (buf_eql_buf(field_node->data.struct_field.name, type_node->data.field_access_expr.field_name)) { + return trans_lookup_ast_container(c, field_node->data.struct_field.type); + } + } + return nullptr; + } else { + return nullptr; + } +} + +static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node) { + if (ref_node->type == NodeTypeSymbol) { + AstNode *existing_node = get_global(c, ref_node->data.symbol_expr.symbol); + if (existing_node == nullptr) + return nullptr; + if (existing_node->type != NodeTypeVariableDeclaration) + return nullptr; + return trans_lookup_ast_container(c, existing_node->data.variable_declaration.type); + } else if (ref_node->type == NodeTypeFieldAccessExpr) { + AstNode *container_node = trans_lookup_ast_container_typeof(c, ref_node->data.field_access_expr.struct_expr); + if (container_node == nullptr) + return nullptr; + if (container_node->type != NodeTypeContainerDecl) + return container_node; + for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) { + AstNode *field_node = container_node->data.container_decl.fields.items[i]; + if (buf_eql_buf(field_node->data.struct_field.name, ref_node->data.field_access_expr.field_name)) { + return trans_lookup_ast_container(c, field_node->data.struct_field.type); + } + } + return nullptr; + } else { + return nullptr; + } +} + +static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) { + AstNode *prefix_node = trans_lookup_ast_container_typeof(c, ref_node); + if (prefix_node == nullptr) + return nullptr; + if (prefix_node->type != NodeTypePrefixOpExpr) + return nullptr; + if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe) + return nullptr; + + AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr; + if (fn_proto_node->type != NodeTypeFnProto) + return nullptr; + + return fn_proto_node; +} + static void render_macros(Context *c) { auto it = c->macro_table.entry_iterator(); for (;;) { @@ -3816,9 +3885,16 @@ static void render_macros(Context *c) { if (!entry) break; + AstNode *proto_node; AstNode *value_node = entry->value; if (value_node->type == NodeTypeFnDef) { add_top_level_decl(c, value_node->data.fn_def.fn_proto->data.fn_proto.name, value_node); + } else if ((proto_node = trans_lookup_ast_maybe_fn(c, value_node))) { + // If a macro aliases a global variable which is a function pointer, we conclude that + // the macro is intended to represent a function that assumes the function pointer + // variable is non-null and calls it. + AstNode *inline_fn_node = trans_create_node_inline_fn(c, entry->key, value_node, proto_node); + add_top_level_decl(c, entry->key, inline_fn_node); } else { add_global_var(c, entry->key, value_node); } @@ -3869,9 +3945,33 @@ static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) { return parse_ctok_num_lit(c, ctok, tok_i, false); case CTokIdSymbol: { - *tok_i += 1; + bool need_symbol = false; + CTokId curr_id = CTokIdSymbol; Buf *symbol_name = buf_create_from_buf(&tok->data.symbol); - return trans_create_node_symbol(c, symbol_name); + AstNode *curr_node = trans_create_node_symbol(c, symbol_name); + AstNode *parent_node = curr_node; + do { + *tok_i += 1; + CTok* curr_tok = &ctok->tokens.at(*tok_i); + if (need_symbol) { + if (curr_tok->id == CTokIdSymbol) { + symbol_name = buf_create_from_buf(&curr_tok->data.symbol); + curr_node = trans_create_node_field_access(c, parent_node, buf_create_from_buf(symbol_name)); + parent_node = curr_node; + need_symbol = false; + } else { + return nullptr; + } + } else { + if (curr_tok->id == CTokIdDot) { + need_symbol = true; + continue; + } else { + break; + } + } + } while (curr_id != CTokIdEOF); + return curr_node; } case CTokIdLParen: { @@ -3885,6 +3985,7 @@ static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) { *tok_i += 1; return inner_node; } + case CTokIdDot: case CTokIdEOF: case CTokIdRParen: // not able to make sense of this @@ -3920,40 +4021,8 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch if (buf_eql_buf(name, symbol_name)) { return; } - c->macro_symbols.append({name, symbol_name}); - } else { - c->macro_table.put(name, result_node); - } -} - -static void process_symbol_macros(Context *c) { - for (size_t i = 0; i < c->macro_symbols.length; i += 1) { - MacroSymbol ms = c->macro_symbols.at(i); - - // Check if this macro aliases another top level declaration - AstNode *existing_node = get_global(c, ms.value); - if (!existing_node || name_exists_global(c, ms.name)) - continue; - - // If a macro aliases a global variable which is a function pointer, we conclude that - // the macro is intended to represent a function that assumes the function pointer - // variable is non-null and calls it. - if (existing_node->type == NodeTypeVariableDeclaration) { - AstNode *var_type = existing_node->data.variable_declaration.type; - if (var_type != nullptr && var_type->type == NodeTypePrefixOpExpr && - var_type->data.prefix_op_expr.prefix_op == PrefixOpMaybe) - { - AstNode *fn_proto_node = var_type->data.prefix_op_expr.primary_expr; - if (fn_proto_node->type == NodeTypeFnProto) { - AstNode *inline_fn_node = trans_create_node_inline_fn(c, ms.name, ms.value, fn_proto_node); - c->macro_table.put(ms.name, inline_fn_node); - continue; - } - } - } - - add_global_var(c, ms.name, trans_create_node_symbol(c, ms.value)); } + c->macro_table.put(name, result_node); } static void process_preprocessor_entities(Context *c, ASTUnit &unit) { @@ -4170,7 +4239,6 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch process_preprocessor_entities(c, *ast_unit); - process_symbol_macros(c); render_macros(c); render_aliases(c); diff --git a/test/translate_c.zig b/test/translate_c.zig index 198d813af1..2fe4944de6 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1046,6 +1046,44 @@ pub fn addCases(cases: &tests.TranslateCContext) { \\ return x + 13; \\} ); + + cases.add("macros with field targets", + \\typedef unsigned int GLbitfield; + \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask); + \\typedef void(*OpenGLProc)(void); + \\union OpenGLProcs { + \\ OpenGLProc ptr[1]; + \\ struct { + \\ PFNGLCLEARPROC Clear; + \\ } gl; + \\}; + \\extern union OpenGLProcs glProcs; + \\#define glClearUnion glProcs.gl.Clear + \\#define glClearPFN PFNGLCLEARPROC + , + \\pub const GLbitfield = c_uint; + , + \\pub const PFNGLCLEARPROC = ?extern fn(GLbitfield); + , + \\pub const OpenGLProc = ?extern fn(); + , + \\pub const union_OpenGLProcs = extern union { + \\ ptr: [1]OpenGLProc, + \\ gl: extern struct { + \\ Clear: PFNGLCLEARPROC, + \\ }, + \\}; + , + \\pub extern var glProcs: union_OpenGLProcs; + , + \\pub const glClearPFN = PFNGLCLEARPROC; + , + \\pub inline fn glClearUnion(arg0: GLbitfield) { + \\ (??glProcs.gl.Clear)(arg0) + \\} + , + \\pub const OpenGLProcs = union_OpenGLProcs; + ); cases.add("switch statement with no default", \\int foo(int x) {