/* * Copyright (c) 2015 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "parseh.hpp" #include "config.h" #include "os.hpp" #include "error.hpp" #include "parser.hpp" #include "all_types.hpp" #include "tokenizer.hpp" #include "analyze.hpp" #include #include #include using namespace clang; struct MacroSymbol { Buf *name; Buf *value; }; struct Context { ImportTableEntry *import; ZigList *errors; bool warnings_on; VisibMod visib_mod; TypeTableEntry *c_void_type; AstNode *root; HashMap global_type_table; HashMap global_value_table; HashMap struct_type_table; HashMap enum_type_table; HashMap fn_table; HashMap macro_table; SourceManager *source_manager; ZigList aliases; ZigList macro_symbols; AstNode *source_node; CodeGen *codegen; }; static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, const Decl *decl, HashMap *type_table); static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl); __attribute__ ((format (printf, 3, 4))) static void emit_warning(Context *c, const Decl *decl, const char *format, ...) { if (!c->warnings_on) { return; } va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); SourceLocation sl = decl->getLocation(); StringRef filename = c->source_manager->getFilename(sl); const char *filename_bytes = (const char *)filename.bytes_begin(); Buf *path; if (filename_bytes) { path = buf_create_from_str(filename_bytes); } else { path = buf_sprintf("(no file)"); } unsigned line = c->source_manager->getSpellingLineNumber(sl); unsigned column = c->source_manager->getSpellingColumnNumber(sl); fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg)); } static AstNode *create_node(Context *c, NodeType type) { AstNode *node = allocate(1); node->type = type; node->owner = c->import; node->create_index = c->codegen->next_node_index; c->codegen->next_node_index += 1; return node; } static AstNode *create_symbol_node(Context *c, const char *type_name) { AstNode *node = create_node(c, NodeTypeSymbol); buf_init_from_str(&node->data.symbol_expr.symbol, type_name); return node; } static AstNode *create_field_access_node(Context *c, const char *lhs, const char *rhs) { AstNode *node = create_node(c, NodeTypeFieldAccessExpr); node->data.field_access_expr.struct_expr = create_symbol_node(c, lhs); buf_init_from_str(&node->data.field_access_expr.field_name, rhs); normalize_parent_ptrs(node); return node; } static ZigList *create_empty_directives(Context *c) { return allocate>(1); } static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char *var_name, AstNode *type_node, AstNode *init_node) { AstNode *node = create_node(c, NodeTypeVariableDeclaration); buf_init_from_str(&node->data.variable_declaration.symbol, var_name); node->data.variable_declaration.is_const = is_const; node->data.variable_declaration.visib_mod = c->visib_mod; node->data.variable_declaration.expr = init_node; node->data.variable_declaration.directives = create_empty_directives(c); node->data.variable_declaration.type = type_node; normalize_parent_ptrs(node); return node; } static AstNode *create_var_decl_node(Context *c, const char *var_name, AstNode *expr_node) { return create_typed_var_decl_node(c, true, var_name, nullptr, expr_node); } static AstNode *create_prefix_node(Context *c, PrefixOp op, AstNode *child_node) { assert(child_node); AstNode *node = create_node(c, NodeTypePrefixOpExpr); node->data.prefix_op_expr.prefix_op = op; node->data.prefix_op_expr.primary_expr = child_node; normalize_parent_ptrs(node); return node; } static AstNode *create_struct_field_node(Context *c, const char *name, AstNode *type_node) { assert(type_node); AstNode *node = create_node(c, NodeTypeStructField); buf_init_from_str(&node->data.struct_field.name, name); node->data.struct_field.directives = create_empty_directives(c); node->data.struct_field.visib_mod = VisibModPub; node->data.struct_field.type = type_node; normalize_parent_ptrs(node); return node; } static AstNode *create_param_decl_node(Context *c, const char *name, AstNode *type_node, bool is_noalias) { assert(type_node); AstNode *node = create_node(c, NodeTypeParamDecl); buf_init_from_str(&node->data.param_decl.name, name); node->data.param_decl.type = type_node; node->data.param_decl.is_noalias = is_noalias; normalize_parent_ptrs(node); return node; } static AstNode *create_char_lit_node(Context *c, uint8_t value) { AstNode *node = create_node(c, NodeTypeCharLiteral); node->data.char_literal.value = value; return node; } static AstNode *create_num_lit_unsigned(Context *c, uint64_t x) { AstNode *node = create_node(c, NodeTypeNumberLiteral); node->data.number_literal.kind = NumLitUInt; node->data.number_literal.data.x_uint = x; return node; } static AstNode *create_num_lit_signed(Context *c, int64_t x) { if (x >= 0) { return create_num_lit_unsigned(c, x); } BigNum bn_orig; bignum_init_signed(&bn_orig, x); BigNum bn_negated; bignum_negate(&bn_negated, &bn_orig); uint64_t uint = bignum_to_twos_complement(&bn_negated); AstNode *num_lit_node = create_num_lit_unsigned(c, uint); return create_prefix_node(c, PrefixOpNegation, num_lit_node); } static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *child_type_node) { AstNode *node = create_node(c, NodeTypeTypeDecl); buf_init_from_str(&node->data.type_decl.symbol, name); node->data.type_decl.visib_mod = c->visib_mod; node->data.type_decl.directives = create_empty_directives(c); node->data.type_decl.child_type = child_type_node; normalize_parent_ptrs(node); return node; } static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) { AstNode *node = create_node(c, NodeTypeSymbol); node->data.symbol_expr.override_type_entry = type_entry; return node; } static const char *decl_name(const Decl *decl) { const NamedDecl *named_decl = static_cast(decl); return (const char *)named_decl->getName().bytes_begin(); } static AstNode *add_typedef_node(Context *c, TypeTableEntry *type_decl) { assert(type_decl); AstNode *node = create_type_decl_node(c, buf_ptr(&type_decl->name), make_type_node(c, type_decl->data.type_decl.child_type)); node->data.type_decl.override_type = type_decl; c->global_type_table.put(&type_decl->name, type_decl); c->root->data.root.top_level_decls.append(node); return node; } static TypeTableEntry *get_c_void_type(Context *c) { if (!c->c_void_type) { c->c_void_type = get_typedecl_type(c->codegen, "c_void", c->codegen->builtin_types.entry_u8); add_typedef_node(c, c->c_void_type); } return c->c_void_type; } static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const Decl *decl, HashMap *type_table) { switch (ty->getTypeClass()) { case Type::Builtin: { const BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { case BuiltinType::Void: return c->codegen->builtin_types.entry_void; case BuiltinType::Bool: return c->codegen->builtin_types.entry_bool; case BuiltinType::Char_U: case BuiltinType::UChar: case BuiltinType::Char_S: return c->codegen->builtin_types.entry_u8; case BuiltinType::SChar: return c->codegen->builtin_types.entry_i8; case BuiltinType::UShort: return get_c_int_type(c->codegen, CIntTypeUShort); case BuiltinType::UInt: return get_c_int_type(c->codegen, CIntTypeUInt); case BuiltinType::ULong: return get_c_int_type(c->codegen, CIntTypeULong); case BuiltinType::ULongLong: return get_c_int_type(c->codegen, CIntTypeULongLong); case BuiltinType::Short: return get_c_int_type(c->codegen, CIntTypeShort); case BuiltinType::Int: return get_c_int_type(c->codegen, CIntTypeInt); case BuiltinType::Long: return get_c_int_type(c->codegen, CIntTypeLong); case BuiltinType::LongLong: return get_c_int_type(c->codegen, CIntTypeLongLong); case BuiltinType::Float: return c->codegen->builtin_types.entry_f32; case BuiltinType::Double: return c->codegen->builtin_types.entry_f64; case BuiltinType::LongDouble: case BuiltinType::WChar_U: case BuiltinType::Char16: case BuiltinType::Char32: case BuiltinType::UInt128: case BuiltinType::WChar_S: case BuiltinType::Int128: case BuiltinType::Half: case BuiltinType::NullPtr: case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: case BuiltinType::OCLImage1d: case BuiltinType::OCLImage1dArray: case BuiltinType::OCLImage1dBuffer: case BuiltinType::OCLImage2d: case BuiltinType::OCLImage2dArray: case BuiltinType::OCLImage3d: case BuiltinType::OCLSampler: case BuiltinType::OCLEvent: case BuiltinType::Dependent: case BuiltinType::Overload: case BuiltinType::BoundMember: case BuiltinType::PseudoObject: case BuiltinType::UnknownAny: case BuiltinType::BuiltinFn: case BuiltinType::ARCUnbridgedCast: emit_warning(c, decl, "missed a builtin type"); return c->codegen->builtin_types.entry_invalid; } break; } case Type::Pointer: { const PointerType *pointer_ty = static_cast(ty); QualType child_qt = pointer_ty->getPointeeType(); TypeTableEntry *child_type = resolve_qual_type(c, child_qt, decl); if (get_underlying_type(child_type)->id == TypeTableEntryIdInvalid) { emit_warning(c, decl, "pointer to unresolved type"); return c->codegen->builtin_types.entry_invalid; } if (child_qt.getTypePtr()->getTypeClass() == Type::Paren) { const ParenType *paren_type = static_cast(child_qt.getTypePtr()); if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) { return get_maybe_type(c->codegen, child_type); } } bool is_const = child_qt.isConstQualified(); if (child_type->id == TypeTableEntryIdVoid) { child_type = get_c_void_type(c); } TypeTableEntry *non_null_pointer_type = get_pointer_to_type(c->codegen, child_type, is_const); return get_maybe_type(c->codegen, non_null_pointer_type); } case Type::Typedef: { const TypedefType *typedef_ty = static_cast(ty); const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); if (buf_eql_str(type_name, "uint8_t")) { return c->codegen->builtin_types.entry_u8; } else if (buf_eql_str(type_name, "int8_t")) { return c->codegen->builtin_types.entry_i8; } else if (buf_eql_str(type_name, "uint16_t")) { return c->codegen->builtin_types.entry_u16; } else if (buf_eql_str(type_name, "int16_t")) { return c->codegen->builtin_types.entry_i16; } else if (buf_eql_str(type_name, "uint32_t")) { return c->codegen->builtin_types.entry_u32; } else if (buf_eql_str(type_name, "int32_t")) { return c->codegen->builtin_types.entry_i32; } else if (buf_eql_str(type_name, "uint64_t")) { return c->codegen->builtin_types.entry_u64; } else if (buf_eql_str(type_name, "int64_t")) { return c->codegen->builtin_types.entry_i64; } else if (buf_eql_str(type_name, "intptr_t")) { return c->codegen->builtin_types.entry_isize; } else if (buf_eql_str(type_name, "uintptr_t")) { return c->codegen->builtin_types.entry_usize; } else { auto entry = type_table->maybe_get(type_name); if (entry) { if (get_underlying_type(entry->value)->id == TypeTableEntryIdInvalid) { return c->codegen->builtin_types.entry_invalid; } else { return entry->value; } } else { return c->codegen->builtin_types.entry_invalid; } } } case Type::Elaborated: { const ElaboratedType *elaborated_ty = static_cast(ty); switch (elaborated_ty->getKeyword()) { case ETK_Struct: return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(), decl, &c->struct_type_table); case ETK_Enum: return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(), decl, &c->enum_type_table); case ETK_Interface: case ETK_Union: case ETK_Class: case ETK_Typename: case ETK_None: emit_warning(c, decl, "unsupported elaborated type"); return c->codegen->builtin_types.entry_invalid; } } case Type::FunctionProto: { const FunctionProtoType *fn_proto_ty = static_cast(ty); switch (fn_proto_ty->getCallConv()) { case CC_C: // __attribute__((cdecl)) break; case CC_X86StdCall: // __attribute__((stdcall)) case CC_X86FastCall: // __attribute__((fastcall)) case CC_X86ThisCall: // __attribute__((thiscall)) case CC_X86VectorCall: // __attribute__((vectorcall)) case CC_X86Pascal: // __attribute__((pascal)) case CC_X86_64Win64: // __attribute__((ms_abi)) case CC_X86_64SysV: // __attribute__((sysv_abi)) case CC_AAPCS: // __attribute__((pcs("aapcs"))) case CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc)) case CC_SpirFunction: // default for OpenCL functions on SPIR target case CC_SpirKernel: // inferred for OpenCL kernels on SPIR target emit_warning(c, decl, "function type has non C calling convention"); return c->codegen->builtin_types.entry_invalid; } FnTypeId fn_type_id; fn_type_id.is_naked = false; fn_type_id.is_extern = true; fn_type_id.is_var_args = fn_proto_ty->isVariadic(); fn_type_id.param_count = fn_proto_ty->getNumParams(); if (fn_proto_ty->getNoReturnAttr()) { fn_type_id.return_type = c->codegen->builtin_types.entry_unreachable; } else { fn_type_id.return_type = resolve_qual_type(c, fn_proto_ty->getReturnType(), decl); if (get_underlying_type(fn_type_id.return_type)->id == TypeTableEntryIdInvalid) { return c->codegen->builtin_types.entry_invalid; } } fn_type_id.param_info = allocate(fn_type_id.param_count); for (int i = 0; i < fn_type_id.param_count; i += 1) { QualType qt = fn_proto_ty->getParamType(i); TypeTableEntry *param_type = resolve_qual_type(c, qt, decl); if (get_underlying_type(param_type)->id == TypeTableEntryIdInvalid) { return c->codegen->builtin_types.entry_invalid; } FnTypeParamInfo *param_info = &fn_type_id.param_info[i]; param_info->type = param_type; param_info->is_noalias = qt.isRestrictQualified(); } return get_fn_type(c->codegen, fn_type_id); } case Type::Record: { const RecordType *record_ty = static_cast(ty); Buf *record_name = buf_create_from_str(decl_name(record_ty->getDecl())); if (buf_len(record_name) == 0) { emit_warning(c, decl, "unhandled anonymous struct"); return c->codegen->builtin_types.entry_invalid; } auto entry = type_table->maybe_get(record_name); if (!entry) { return c->codegen->builtin_types.entry_invalid; } return entry->value; } case Type::Enum: { const EnumType *enum_ty = static_cast(ty); Buf *record_name = buf_create_from_str(decl_name(enum_ty->getDecl())); if (buf_len(record_name) == 0) { emit_warning(c, decl, "unhandled anonymous enum"); return c->codegen->builtin_types.entry_invalid; } auto entry = type_table->maybe_get(record_name); if (!entry) { return c->codegen->builtin_types.entry_invalid; } return entry->value; } case Type::ConstantArray: { const ConstantArrayType *const_arr_ty = static_cast(ty); TypeTableEntry *child_type = resolve_qual_type(c, const_arr_ty->getElementType(), decl); uint64_t size = const_arr_ty->getSize().getLimitedValue(); return get_array_type(c->codegen, child_type, size); } case Type::Paren: { const ParenType *paren_ty = static_cast(ty); return resolve_qual_type(c, paren_ty->getInnerType(), decl); } case Type::Decayed: { const DecayedType *decayed_ty = static_cast(ty); return resolve_qual_type(c, decayed_ty->getDecayedType(), decl); } case Type::BlockPointer: case Type::LValueReference: case Type::RValueReference: case Type::MemberPointer: case Type::IncompleteArray: case Type::VariableArray: case Type::DependentSizedArray: case Type::DependentSizedExtVector: case Type::Vector: case Type::ExtVector: case Type::FunctionNoProto: case Type::UnresolvedUsing: case Type::Adjusted: case Type::TypeOfExpr: case Type::TypeOf: case Type::Decltype: case Type::UnaryTransform: case Type::Attributed: case Type::TemplateTypeParm: case Type::SubstTemplateTypeParm: case Type::SubstTemplateTypeParmPack: case Type::TemplateSpecialization: case Type::Auto: case Type::InjectedClassName: case Type::DependentName: case Type::DependentTemplateSpecialization: case Type::PackExpansion: case Type::ObjCObject: case Type::ObjCInterface: case Type::Complex: case Type::ObjCObjectPointer: case Type::Atomic: emit_warning(c, decl, "missed a '%s' type", ty->getTypeClassName()); return c->codegen->builtin_types.entry_invalid; } } static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, const Decl *decl, HashMap *type_table) { return resolve_type_with_table(c, qt.getTypePtr(), decl, type_table); } static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl) { return resolve_qual_type_with_table(c, qt, decl, &c->global_type_table); } static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { Buf fn_name = BUF_INIT; buf_init_from_str(&fn_name, decl_name(fn_decl)); if (c->fn_table.maybe_get(&fn_name)) { // we already saw this function return; } TypeTableEntry *fn_type = resolve_qual_type(c, fn_decl->getType(), fn_decl); if (fn_type->id == TypeTableEntryIdInvalid) { emit_warning(c, fn_decl, "ignoring function '%s' - unable to resolve type", buf_ptr(&fn_name)); return; } assert(fn_type->id == TypeTableEntryIdFn); AstNode *node = create_node(c, NodeTypeFnProto); buf_init_from_buf(&node->data.fn_proto.name, &fn_name); node->data.fn_proto.is_extern = fn_type->data.fn.fn_type_id.is_extern; node->data.fn_proto.visib_mod = c->visib_mod; node->data.fn_proto.directives = create_empty_directives(c); node->data.fn_proto.is_var_args = fn_type->data.fn.fn_type_id.is_var_args; node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); assert(!fn_type->data.fn.fn_type_id.is_naked); int arg_count = fn_type->data.fn.fn_type_id.param_count; Buf name_buf = BUF_INIT; for (int i = 0; i < arg_count; i += 1) { FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[i]; AstNode *type_node = make_type_node(c, param_info->type); const ParmVarDecl *param = fn_decl->getParamDecl(i); const char *name = decl_name(param); if (strlen(name) == 0) { buf_resize(&name_buf, 0); buf_appendf(&name_buf, "arg%d", i); name = buf_ptr(&name_buf); } node->data.fn_proto.params.append(create_param_decl_node(c, name, type_node, param_info->is_noalias)); } normalize_parent_ptrs(node); c->fn_table.put(buf_create_from_buf(&fn_name), true); c->root->data.root.top_level_decls.append(node); } static void visit_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl) { QualType child_qt = typedef_decl->getUnderlyingType(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); if (buf_eql_str(type_name, "uint8_t") || buf_eql_str(type_name, "int8_t") || buf_eql_str(type_name, "uint16_t") || buf_eql_str(type_name, "int16_t") || buf_eql_str(type_name, "uint32_t") || buf_eql_str(type_name, "int32_t") || buf_eql_str(type_name, "uint64_t") || buf_eql_str(type_name, "int64_t") || buf_eql_str(type_name, "intptr_t") || buf_eql_str(type_name, "uintptr_t")) { // special case we can just use the builtin types return; } // if the underlying type is anonymous, we can special case it to just // use the name of this typedef // TODO TypeTableEntry *child_type = resolve_qual_type(c, child_qt, typedef_decl); TypeTableEntry *decl_type = get_typedecl_type(c->codegen, buf_ptr(type_name), child_type); add_typedef_node(c, decl_type); } static void add_alias(Context *c, const char *new_name, const char *target_name) { AstNode *alias_node = create_var_decl_node(c, new_name, create_symbol_node(c, target_name)); c->aliases.append(alias_node); } static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { const char *raw_name = decl_name(enum_decl); // we have no interest in top level anonymous enums since they're // not exposing anything. if (raw_name[0] == 0) { return; } Buf *bare_name = buf_create_from_str(raw_name); Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name)); if (c->enum_type_table.maybe_get(bare_name)) { // we've already seen it return; } const EnumDecl *enum_def = enum_decl->getDefinition(); if (!enum_def) { TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name), c->codegen->builtin_types.entry_u8); c->enum_type_table.put(bare_name, typedecl_type); // this is a type that we can point to but that's it, same as `struct Foo;`. add_typedef_node(c, typedecl_type); add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); return; } // count and validate uint32_t field_count = 0; for (auto it = enum_def->enumerator_begin(), it_end = enum_def->enumerator_end(); it != it_end; ++it, field_count += 1) { const EnumConstantDecl *enum_const = *it; if (enum_const->getInitExpr()) { emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(bare_name)); return; } } TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import, ContainerKindEnum, c->source_node, buf_ptr(full_type_name)); enum_type->data.enumeration.gen_field_count = 0; enum_type->data.enumeration.complete = true; TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(c->codegen, field_count); enum_type->align_in_bits = tag_type_entry->size_in_bits; enum_type->size_in_bits = tag_type_entry->size_in_bits; enum_type->data.enumeration.tag_type = tag_type_entry; c->enum_type_table.put(bare_name, enum_type); // make an alias without the "enum_" prefix. this will get emitted at the // end if it doesn't conflict with anything else add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); enum_type->data.enumeration.field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); LLVMZigDIEnumerator **di_enumerators = allocate(field_count); ZigList var_decls = {0}; uint32_t i = 0; for (auto it = enum_def->enumerator_begin(), it_end = enum_def->enumerator_end(); it != it_end; ++it, i += 1) { const EnumConstantDecl *enum_const = *it; Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); Buf *field_name; if (buf_starts_with_buf(enum_val_name, bare_name)) { Buf *slice = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); if (valid_symbol_starter(buf_ptr(slice)[0])) { field_name = slice; } else { field_name = buf_sprintf("_%s", buf_ptr(slice)); } } else { field_name = enum_val_name; } TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; type_enum_field->name = field_name; type_enum_field->type_entry = c->codegen->builtin_types.entry_void; type_enum_field->value = i; di_enumerators[i] = LLVMZigCreateDebugEnumerator(c->codegen->dbuilder, buf_ptr(type_enum_field->name), i); // in C each enum value is in the global namespace. so we put them there too. // at this point we can rely on the enum emitting successfully AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(field_name)); AstNode *var_node = create_var_decl_node(c, buf_ptr(enum_val_name), field_access_node); var_decls.append(var_node); c->global_value_table.put(enum_val_name, enum_type); } // create llvm type for root struct enum_type->type_ref = tag_type_entry->type_ref; // create debug type for tag unsigned line = c->source_node ? (c->source_node->line + 1) : 0; LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(c->codegen->dbuilder, LLVMZigFileToScope(c->import->di_file), buf_ptr(bare_name), c->import->di_file, line, tag_type_entry->size_in_bits, tag_type_entry->align_in_bits, di_enumerators, field_count, tag_type_entry->di_type, ""); LLVMZigReplaceTemporary(c->codegen->dbuilder, enum_type->di_type, tag_di_type); enum_type->di_type = tag_di_type; ////////// // now create top level decl for the type AstNode *enum_node = create_node(c, NodeTypeStructDecl); buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name); enum_node->data.struct_decl.kind = ContainerKindEnum; enum_node->data.struct_decl.visib_mod = VisibModExport; enum_node->data.struct_decl.directives = create_empty_directives(c); enum_node->data.struct_decl.type_entry = enum_type; for (uint32_t i = 0; i < field_count; i += 1) { TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; AstNode *type_node = make_type_node(c, type_enum_field->type_entry); AstNode *field_node = create_struct_field_node(c, buf_ptr(type_enum_field->name), type_node); enum_node->data.struct_decl.fields.append(field_node); } normalize_parent_ptrs(enum_node); c->root->data.root.top_level_decls.append(enum_node); for (int i = 0; i < var_decls.length; i += 1) { AstNode *var_node = var_decls.at(i); c->root->data.root.top_level_decls.append(var_node); } } static void visit_record_decl(Context *c, const RecordDecl *record_decl) { const char *raw_name = decl_name(record_decl); // we have no interest in top level anonymous structs since they're // not exposing anything. if (record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0) { return; } if (!record_decl->isStruct()) { emit_warning(c, record_decl, "skipping record %s, not a struct", raw_name); return; } Buf *bare_name = buf_create_from_str(raw_name); Buf *full_type_name = buf_sprintf("struct_%s", buf_ptr(bare_name)); if (c->struct_type_table.maybe_get(bare_name)) { // we've already seen it return; } RecordDecl *record_def = record_decl->getDefinition(); if (!record_def) { TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name), c->codegen->builtin_types.entry_u8); c->struct_type_table.put(bare_name, typedecl_type); // this is a type that we can point to but that's it, such as `struct Foo;`. add_typedef_node(c, typedecl_type); add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); return; } TypeTableEntry *struct_type = get_partial_container_type(c->codegen, c->import, ContainerKindStruct, c->source_node, buf_ptr(full_type_name)); c->struct_type_table.put(bare_name, struct_type); // make an alias without the "struct_" prefix. this will get emitted at the // end if it doesn't conflict with anything else add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); // count fields and validate uint32_t field_count = 0; for (auto it = record_def->field_begin(), it_end = record_def->field_end(); it != it_end; ++it, field_count += 1) { const FieldDecl *field_decl = *it; if (field_decl->isBitField()) { emit_warning(c, field_decl, "skipping struct %s - has bitfield\n", buf_ptr(bare_name)); return; } } struct_type->data.structure.src_field_count = field_count; struct_type->data.structure.fields = allocate(field_count); // we possibly allocate too much here since gen_field_count can be lower than field_count. // the only problem is potential wasted space though. LLVMTypeRef *element_types = allocate(field_count); LLVMZigDIType **di_element_types = allocate(field_count); uint64_t total_size_in_bits = 0; uint64_t first_field_align_in_bits = 0; uint64_t offset_in_bits = 0; uint32_t i = 0; unsigned line = c->source_node ? c->source_node->line : 0; for (auto it = record_def->field_begin(), it_end = record_def->field_end(); it != it_end; ++it, i += 1) { const FieldDecl *field_decl = *it; TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = buf_create_from_str(decl_name(field_decl)); type_struct_field->src_index = i; type_struct_field->gen_index = i; type_struct_field->type_entry = resolve_qual_type(c, field_decl->getType(), field_decl); if (type_struct_field->type_entry->id == TypeTableEntryIdInvalid) { emit_warning(c, field_decl, "skipping struct %s - unresolved type\n", buf_ptr(bare_name)); return; } di_element_types[i] = LLVMZigCreateDebugMemberType(c->codegen->dbuilder, LLVMZigTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), c->import->di_file, line + 1, type_struct_field->type_entry->size_in_bits, type_struct_field->type_entry->align_in_bits, offset_in_bits, 0, type_struct_field->type_entry->di_type); element_types[i] = type_struct_field->type_entry->type_ref; assert(di_element_types[i]); assert(element_types[i]); total_size_in_bits += type_struct_field->type_entry->size_in_bits; if (first_field_align_in_bits == 0) { first_field_align_in_bits = type_struct_field->type_entry->align_in_bits; } offset_in_bits += type_struct_field->type_entry->size_in_bits; } struct_type->data.structure.embedded_in_current = false; struct_type->data.structure.gen_field_count = field_count; struct_type->data.structure.complete = true; LLVMStructSetBody(struct_type->type_ref, element_types, field_count, false); struct_type->align_in_bits = first_field_align_in_bits; struct_type->size_in_bits = total_size_in_bits; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(c->codegen->dbuilder, LLVMZigFileToScope(c->import->di_file), buf_ptr(full_type_name), c->import->di_file, line + 1, struct_type->size_in_bits, struct_type->align_in_bits, 0, nullptr, di_element_types, field_count, 0, nullptr, ""); LLVMZigReplaceTemporary(c->codegen->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; ////// // now create a top level decl node for the type AstNode *struct_node = create_node(c, NodeTypeStructDecl); buf_init_from_buf(&struct_node->data.struct_decl.name, full_type_name); struct_node->data.struct_decl.kind = ContainerKindStruct; struct_node->data.struct_decl.visib_mod = VisibModExport; struct_node->data.struct_decl.directives = create_empty_directives(c); struct_node->data.struct_decl.type_entry = struct_type; for (uint32_t i = 0; i < field_count; i += 1) { TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; AstNode *type_node = make_type_node(c, type_struct_field->type_entry); AstNode *field_node = create_struct_field_node(c, buf_ptr(type_struct_field->name), type_node); struct_node->data.struct_decl.fields.append(field_node); } normalize_parent_ptrs(struct_node); c->root->data.root.top_level_decls.append(struct_node); } static void visit_var_decl(Context *c, const VarDecl *var_decl) { Buf *name = buf_create_from_str(decl_name(var_decl)); switch (var_decl->getTLSKind()) { case VarDecl::TLS_None: break; case VarDecl::TLS_Static: emit_warning(c, var_decl, "ignoring variable '%s' - static thread local storage\n", buf_ptr(name)); return; case VarDecl::TLS_Dynamic: emit_warning(c, var_decl, "ignoring variable '%s' - dynamic thread local storage\n", buf_ptr(name)); return; } QualType qt = var_decl->getType(); TypeTableEntry *var_type = resolve_qual_type(c, qt, var_decl); if (var_type->id == TypeTableEntryIdInvalid) { emit_warning(c, var_decl, "ignoring variable '%s' - unresolved type\n", buf_ptr(name)); return; } bool is_extern = var_decl->hasExternalStorage(); bool is_static = var_decl->isFileVarDecl(); bool is_const = qt.isConstQualified(); if (is_static && !is_extern) { if (!var_decl->hasInit()) { emit_warning(c, var_decl, "ignoring variable '%s' - no initializer\n", buf_ptr(name)); return; } APValue *ap_value = var_decl->evaluateValue(); if (!ap_value) { emit_warning(c, var_decl, "ignoring variable '%s' - unable to evaluate initializer\n", buf_ptr(name)); return; } AstNode *init_node; switch (ap_value->getKind()) { case APValue::Int: { TypeTableEntry *canon_type = get_underlying_type(var_type); if (canon_type->id != TypeTableEntryIdInt) { emit_warning(c, var_decl, "ignoring variable '%s' - int initializer for non int type\n", buf_ptr(name)); return; } llvm::APSInt aps_int = ap_value->getInt(); if (aps_int.isSigned()) { if (aps_int > INT64_MAX || aps_int < INT64_MIN) { emit_warning(c, var_decl, "ignoring variable '%s' - initializer overflow\n", buf_ptr(name)); return; } else { init_node = create_num_lit_signed(c, aps_int.getExtValue()); } } else { if (aps_int > UINT64_MAX) { emit_warning(c, var_decl, "ignoring variable '%s' - initializer overflow\n", buf_ptr(name)); return; } else { init_node = create_num_lit_unsigned(c, aps_int.getExtValue()); } } break; } case APValue::Uninitialized: case APValue::Float: case APValue::ComplexInt: case APValue::ComplexFloat: case APValue::LValue: case APValue::Vector: case APValue::Array: case APValue::Struct: case APValue::Union: case APValue::MemberPointer: case APValue::AddrLabelDiff: emit_warning(c, var_decl, "ignoring variable '%s' - unrecognized initializer value kind\n", buf_ptr(name)); return; } AstNode *type_node = make_type_node(c, var_type); AstNode *var_node = create_typed_var_decl_node(c, true, buf_ptr(name), type_node, init_node); c->root->data.root.top_level_decls.append(var_node); c->global_value_table.put(name, var_type); return; } if (is_extern) { AstNode *type_node = make_type_node(c, var_type); AstNode *var_node = create_typed_var_decl_node(c, is_const, buf_ptr(name), type_node, nullptr); var_node->data.variable_declaration.is_extern = true; c->root->data.root.top_level_decls.append(var_node); c->global_value_table.put(name, var_type); return; } emit_warning(c, var_decl, "ignoring variable '%s' - non-extern, non-static variable\n", buf_ptr(name)); return; } static bool decl_visitor(void *context, const Decl *decl) { Context *c = (Context*)context; switch (decl->getKind()) { case Decl::Function: visit_fn_decl(c, static_cast(decl)); break; case Decl::Typedef: visit_typedef_decl(c, static_cast(decl)); break; case Decl::Enum: visit_enum_decl(c, static_cast(decl)); break; case Decl::Record: visit_record_decl(c, static_cast(decl)); break; case Decl::Var: visit_var_decl(c, static_cast(decl)); break; default: emit_warning(c, decl, "ignoring %s decl\n", decl->getDeclKindName()); } return true; } static bool name_exists(Context *c, Buf *name) { if (c->global_type_table.maybe_get(name)) { return true; } if (c->global_value_table.maybe_get(name)) { return true; } if (c->fn_table.maybe_get(name)) { return true; } if (c->macro_table.maybe_get(name)) { return true; } return false; } static void render_aliases(Context *c) { for (int i = 0; i < c->aliases.length; i += 1) { AstNode *alias_node = c->aliases.at(i); assert(alias_node->type == NodeTypeVariableDeclaration); Buf *name = &alias_node->data.variable_declaration.symbol; if (name_exists(c, name)) { continue; } c->root->data.root.top_level_decls.append(alias_node); } } static void render_macros(Context *c) { auto it = c->macro_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; AstNode *var_node = entry->value; c->root->data.root.top_level_decls.append(var_node); } } static int parse_c_char_lit(Buf *value, uint8_t *out_c) { enum State { StateExpectStartQuot, StateExpectChar, StateExpectEndQuot, StateExpectEnd, }; State state = StateExpectStartQuot; for (int i = 0; i < buf_len(value); i += 1) { uint8_t c = buf_ptr(value)[i]; switch (state) { case StateExpectStartQuot: switch (c) { case '\'': state = StateExpectChar; break; default: return -1; } break; case StateExpectChar: switch (c) { case '\\': case '\'': return -1; default: *out_c = c; state = StateExpectEndQuot; } break; case StateExpectEndQuot: switch (c) { case '\'': state = StateExpectEnd; break; default: return -1; } break; case StateExpectEnd: return -1; } } return (state == StateExpectEnd) ? 0 : -1; } static int parse_c_num_lit_unsigned(Buf *buf, uint64_t *out_val) { char *temp; *out_val = strtoull(buf_ptr(buf), &temp, 0); if (temp == buf_ptr(buf) || *temp != 0 || *out_val == ULLONG_MAX) { return -1; } return 0; } static bool is_simple_symbol(Buf *buf) { bool first = true; for (int i = 0; i < buf_len(buf); i += 1) { uint8_t c = buf_ptr(buf)[i]; bool valid_alpha = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; bool valid_digit = (c >= '0' && c <= '9'); bool ok = (valid_alpha || (!first && valid_digit)); first = false; if (!ok) { return false; } } return true; } static void process_macro(Context *c, Buf *name, Buf *value) { //fprintf(stderr, "macro '%s' = '%s'\n", buf_ptr(name), buf_ptr(value)); if (is_zig_keyword(name)) { return; } // maybe it's a character literal uint8_t ch; if (!parse_c_char_lit(value, &ch)) { AstNode *var_node = create_var_decl_node(c, buf_ptr(name), create_char_lit_node(c, ch)); c->macro_table.put(name, var_node); return; } // maybe it's a string literal // TODO // maybe it's an unsigned integer uint64_t uint; if (!parse_c_num_lit_unsigned(value, &uint)) { AstNode *var_node = create_var_decl_node(c, buf_ptr(name), create_num_lit_unsigned(c, uint)); c->macro_table.put(name, var_node); return; } // maybe it's a symbol if (is_simple_symbol(value)) { // if it equals itself, ignore. for example, from stdio.h: // #define stdin stdin if (buf_eql_buf(name, value)) { return; } c->macro_symbols.append({name, value}); } } static void process_symbol_macros(Context *c) { for (int i = 0; i < c->macro_symbols.length; i += 1) { MacroSymbol ms = c->macro_symbols.at(i); if (name_exists(c, ms.value)) { AstNode *var_node = create_var_decl_node(c, buf_ptr(ms.name), create_symbol_node(c, buf_ptr(ms.value))); c->macro_table.put(ms.name, var_node); } } } static void process_preprocessor_entities(Context *c, ASTUnit &unit) { for (PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) { switch (entity->getKind()) { case PreprocessedEntity::InvalidKind: case PreprocessedEntity::InclusionDirectiveKind: case PreprocessedEntity::MacroExpansionKind: continue; case PreprocessedEntity::MacroDefinitionKind: { MacroDefinitionRecord *macro = static_cast(entity); const char *name = macro->getName()->getNameStart(); SourceRange range = macro->getSourceRange(); SourceLocation begin_loc = range.getBegin(); SourceLocation end_loc = range.getEnd(); if (begin_loc == end_loc) { // this means it is a macro without a value // we don't care about such things continue; } const char *end_c = c->source_manager->getCharacterData(end_loc); Buf *value = buf_alloc(); while (*end_c && *end_c != '\n') { buf_append_char(value, *end_c); if (end_c[0] == '\\' && end_c[1] == '\n') { end_c += 2; } else { end_c += 1; } } process_macro(c, buf_create_from_str(name), value); } } } } int parse_h_buf(ImportTableEntry *import, ZigList *errors, Buf *source, CodeGen *codegen, AstNode *source_node) { int err; Buf tmp_file_path = BUF_INIT; if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) { return err; } err = parse_h_file(import, errors, buf_ptr(&tmp_file_path), codegen, source_node); os_delete_file(&tmp_file_path); return err; } int parse_h_file(ImportTableEntry *import, ZigList *errors, const char *target_file, CodeGen *codegen, AstNode *source_node) { Context context = {0}; Context *c = &context; c->warnings_on = codegen->verbose; c->import = import; c->errors = errors; c->visib_mod = VisibModPub; c->global_type_table.init(8); c->global_value_table.init(8); c->enum_type_table.init(8); c->struct_type_table.init(8); c->fn_table.init(8); c->macro_table.init(8); c->codegen = codegen; c->source_node = source_node; ZigList clang_argv = {0}; clang_argv.append("-x"); clang_argv.append("c"); char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); if (ZIG_PARSEH_CFLAGS) { Buf tmp_buf = BUF_INIT; char *start = ZIG_PARSEH_CFLAGS; char *space = strstr(start, " "); while (space) { if (space - start > 0) { buf_init_from_mem(&tmp_buf, start, space - start); clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf))); } start = space + 1; space = strstr(start, " "); } buf_init_from_str(&tmp_buf, start); clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf))); } clang_argv.append("-isystem"); clang_argv.append(ZIG_HEADERS_DIR); clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->libc_include_path)); for (int i = 0; i < codegen->clang_argv_len; i += 1) { clang_argv.append(codegen->clang_argv[i]); } // we don't need spell checking and it slows things down clang_argv.append("-fno-spell-checking"); // this gives us access to preprocessing entities, presumably at // the cost of performance clang_argv.append("-Xclang"); clang_argv.append("-detailed-preprocessing-record"); clang_argv.append(target_file); // to make the [start...end] argument work clang_argv.append(nullptr); IntrusiveRefCntPtr diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); std::shared_ptr pch_container_ops = std::make_shared(); bool skip_function_bodies = true; bool only_local_decls = true; bool capture_diagnostics = true; bool user_files_are_volatile = true; bool allow_pch_with_compiler_errors = false; const char *resources_path = ZIG_HEADERS_DIR; std::unique_ptr err_unit; std::unique_ptr ast_unit(ASTUnit::LoadFromCommandLine( &clang_argv.at(0), &clang_argv.last(), pch_container_ops, diags, resources_path, only_local_decls, capture_diagnostics, None, true, false, TU_Complete, false, false, allow_pch_with_compiler_errors, skip_function_bodies, user_files_are_volatile, false, &err_unit)); // Early failures in LoadFromCommandLine may return with ErrUnit unset. if (!ast_unit && !err_unit) { return ErrorFileSystem; } if (diags->getClient()->getNumErrors() > 0) { if (ast_unit) { err_unit = std::move(ast_unit); } for (ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), it_end = err_unit->stored_diag_end(); it != it_end; ++it) { switch (it->getLevel()) { case DiagnosticsEngine::Ignored: case DiagnosticsEngine::Note: case DiagnosticsEngine::Remark: case DiagnosticsEngine::Warning: continue; case DiagnosticsEngine::Error: case DiagnosticsEngine::Fatal: break; } StringRef msg_str_ref = it->getMessage(); FullSourceLoc fsl = it->getLocation(); FileID file_id = fsl.getFileID(); StringRef filename = fsl.getManager().getFilename(fsl); unsigned line = fsl.getSpellingLineNumber() - 1; unsigned column = fsl.getSpellingColumnNumber() - 1; unsigned offset = fsl.getManager().getFileOffset(fsl); const char *source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin(); Buf *msg = buf_create_from_str((const char *)msg_str_ref.bytes_begin()); Buf *path = buf_create_from_str((const char *)filename.bytes_begin()); ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg); c->errors->append(err_msg); } return 0; } c->source_manager = &ast_unit->getSourceManager(); c->root = create_node(c, NodeTypeRoot); ast_unit->visitLocalTopLevelDecls(c, decl_visitor); process_preprocessor_entities(c, *ast_unit); process_symbol_macros(c); render_macros(c); render_aliases(c); normalize_parent_ptrs(c->root); import->root = c->root; return 0; }