From e4b0435946ded78b2d4664bf4cf3cf387fe4a12e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Jan 2016 16:06:17 -0700 Subject: [PATCH] parseh understands variable declarations and some initializers such as integers --- doc/langref.md | 2 +- src/analyze.cpp | 5 +- src/parseh.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++-- test/run_tests.cpp | 8 +++ 4 files changed, 144 insertions(+), 6 deletions(-) diff --git a/doc/langref.md b/doc/langref.md index 4ae1a2c0c8..3bcf50b6d8 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -25,7 +25,7 @@ Import = "import" "String" ";" RootExportDecl = "export" "Symbol" "String" ";" -ExternDecl = "extern" FnProto ";" +ExternDecl = "extern" (FnProto | VariableDeclaration) ";" FnProto = "fn" option("Symbol") ParamDeclList option("->" PrefixOpExpression) diff --git a/src/analyze.cpp b/src/analyze.cpp index 38b2a8faca..88c9ba0323 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -90,6 +90,8 @@ static AstNode *first_executing_node(AstNode *node) { } ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { + // if this assert fails, then parseh generated code that + // failed semantic analysis, which isn't supposed to happen assert(!node->owner->c_import_node); ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, @@ -2768,6 +2770,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa { bool is_const = variable_declaration->is_const; bool is_export = (variable_declaration->visib_mod == VisibModExport); + bool is_extern = variable_declaration->is_extern; TypeTableEntry *explicit_type = nullptr; if (variable_declaration->type != nullptr) { @@ -2812,7 +2815,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa buf_sprintf("global variable initializer requires constant expression")); } } - } else { + } else if (!is_extern) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; } diff --git a/src/parseh.cpp b/src/parseh.cpp index bc5d690b39..743531ea7e 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -97,17 +97,24 @@ static ZigList *create_empty_directives(Context *c) { return allocate>(1); } -static AstNode *create_var_decl_node(Context *c, const char *var_name, AstNode *expr_node) { +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 = true; + node->data.variable_declaration.is_const = is_const; node->data.variable_declaration.visib_mod = c->visib_mod; - node->data.variable_declaration.expr = expr_node; + 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); @@ -150,10 +157,24 @@ 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_array_type_node(Context *c, AstNode *child_type_node, uint64_t size, bool is_const) { AstNode *node = create_node(c, NodeTypeArrayType); node->data.array_type.size = create_num_lit_unsigned(c, size); @@ -202,6 +223,11 @@ static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) { return create_prefix_node(c, PrefixOpMaybe, child_node); } +static bool type_is_int(AstNode *type_node) { + // TODO recurse through the type table + return true; +} + static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl, HashMap *type_table) { @@ -689,6 +715,104 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) { add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name)); } +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(); + AstNode *type_node = make_qual_type_node(c, qt, var_decl); + if (!type_node) { + 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: + { + if (!type_is_int(type_node)) { + 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 *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->root_name_table.put(name, true); + return; + } + + if (is_extern) { + 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->root_name_table.put(name, true); + 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; @@ -705,6 +829,9 @@ static bool decl_visitor(void *context, const Decl *decl) { 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()); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index ba991b163f..b5afdabfcd 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -2011,6 +2011,14 @@ pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;)OUTPUT", )SOURCE", 2, "pub const THING1 = 1234;", "pub const THING2 = THING1;"); + + + add_parseh_case("variables", R"SOURCE( +extern int extern_var; +static const int int_var = 13; + )SOURCE", 2, + "pub extern var extern_var: c_int;", + "pub const int_var: c_int = 13;"); } static void print_compiler_invocation(TestCase *test_case) {