diff --git a/CHANGELOG.md b/CHANGELOG.md index 8223c8f2d6cf8b552c37873f9ace9ebf4d9a6455..2142c8900d2f3193ff4652be4bb44e9ac509b4aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Allow iterating a range in macros with `for`. * Use cpu cycle counter to initialize random. * `method_missing` now works in generic types. +* Fixed [#154](https://github.com/manastech/crystal/issues/154): bug, constants are initialized before global variables. * Fixed [#168](https://github.com/manastech/crystal/issues/168): incorrect type inference of instance variables if not assigned in superclass. * Fixed [#169](https://github.com/manastech/crystal/issues/169): `responds_to?` wasn't working with generic types. * Fixed [#171](https://github.com/manastech/crystal/issues/171): ensure blocks are not executed if the rescue block returns from a def. diff --git a/spec/compiler/codegen/const_spec.cr b/spec/compiler/codegen/const_spec.cr index d9288c2d06376bef241fbc63a87122aae41234f8..4c631810ec5ad3d5a8dd23aadb0b383d5f332281 100644 --- a/spec/compiler/codegen/const_spec.cr +++ b/spec/compiler/codegen/const_spec.cr @@ -200,4 +200,12 @@ describe "Codegen: const" do (A + B).to_i ").to_i.should eq(3) end + + it "works with const initialized after global variable" do + run(%( + $a = 1 + COCO = $a + COCO + )).to_i.should eq(1) + end end diff --git a/src/compiler/crystal/codegen.cr b/src/compiler/crystal/codegen.cr index 1a9f0d68f920000dd899977833dbfa6c95def64f..6ee1e2a3b6b45adf6993711a91f9c1085be4e5c1 100644 --- a/src/compiler/crystal/codegen.cr +++ b/src/compiler/crystal/codegen.cr @@ -129,17 +129,17 @@ module Crystal LLVM.set_name @argv, "argv" builder = LLVM::Builder.new - @builder = CrystalLLVMBuilder.new builder, self + @builder = wrap_builder builder @dbg_kind = LibLLVM.get_md_kind_id("dbg", 3_u32) @modules = {"" => @main_mod} of String => LLVM::Module @types_to_modules = {} of Type => LLVM::Module - @alloca_block, @const_block, @entry_block = new_entry_block_chain({"alloca", "const", "entry"}) + @alloca_block, @entry_block = new_entry_block_chain({"alloca", "entry"}) @main_alloca_block = @alloca_block - @const_block_entry = @const_block + @in_lib = false @strings = {} of StringKey => LibLLVM::ValueRef @symbols = {} of String => Int32 @symbol_table_values = [] of LibLLVM::ValueRef @@ -167,6 +167,17 @@ module Crystal @subprograms[@main_mod] = [fun_metadata(context.fun, MAIN_NAME, "foo.cr", 1)] if @debug alloca_vars @mod.vars, @mod + + declare_const(@mod.types["ARGC_UNSAFE"] as Const) + declare_const(@mod.types["ARGV_UNSAFE"] as Const) + declare_const(@mod.types["Float32"].types["INFINITY"] as Const) + declare_const(@mod.types["Float64"].types["INFINITY"] as Const) + + @unused_fun_defs = [] of FunDef + end + + def wrap_builder(builder) + CrystalLLVMBuilder.new builder, self end def define_symbol_table(llvm_mod) @@ -182,12 +193,14 @@ module Crystal # If there are no instructions in the alloca block and the # const block, we just removed them (less noise) - if LLVM.first_instruction(@alloca_block) || LLVM.first_instruction(@const_block_entry) - br_block_chain [@alloca_block, @const_block_entry] - br_block_chain [@const_block, @entry_block] + if LLVM.first_instruction(@alloca_block) + br_block_chain [@alloca_block, @entry_block] else LLVM.delete_basic_block(@alloca_block) - LLVM.delete_basic_block(@const_block_entry) + end + + @unused_fun_defs.each do |node| + codegen_fun node.real_name, node.external, @mod, true end env_dump = ENV["DUMP"]? @@ -212,8 +225,22 @@ module Crystal end def visit(node : FunDef) + if @in_lib + return false + end + unless node.external.dead - codegen_fun node.real_name, node.external, @mod, true + if node.external.used + codegen_fun node.real_name, node.external, @mod, true + else + # If the fun is not invoked we codegen it at the end so + # we don't have issues with constants being used before + # they are declared. + # But, apparenty, llvm requires us to define them so that + # calls can find them, so we do so. + codegen_fun node.real_name, node.external, @mod, false + @unused_fun_defs << node + end end false @@ -418,6 +445,37 @@ module Crystal end def visit(node : LibDef) + @in_lib = true + node.body.accept self + @in_lib = false + @last = llvm_nil + false + end + + def visit(node : StructDef) + @last = llvm_nil + false + end + + def visit(node : UnionDef) + @last = llvm_nil + false + end + + def visit(node : EnumDef) + node.c_enum_type.types.each_value do |type| + declare_const(type as Const) + end + @last = llvm_nil + false + end + + def visit(node : ExternalVar) + @last = llvm_nil + false + end + + def visit(node : TypeDef) @last = llvm_nil false end @@ -566,7 +624,10 @@ module Crystal def visit(node : Assign) target, value = node.target, node.value + # Initialize constants if they are used if target.is_a?(Path) + const = target.target_const.not_nil! + declare_const(const) @last = llvm_nil return false end @@ -800,44 +861,7 @@ module Crystal def visit(node : Path) if const = node.target_const global_name = const.llvm_name - global = @main_mod.globals[global_name]? - - unless global - global = @main_mod.globals.add(llvm_type(const.value.type), global_name) - - if const.value.needs_const_block? - in_const_block("const_#{global_name}", const.container) do - alloca_vars const.vars - - request_value do - accept const.value - end - - if LLVM.constant? @last - LLVM.set_initializer global, @last - LLVM.set_global_constant global, true - else - if const.value.type.passed_by_value? - @last = load @last - LLVM.set_initializer global, LLVM.undef(llvm_type(const.value.type)) - else - LLVM.set_initializer global, LLVM.null(type_of @last) - end - - store @last, global - end - end - else - old_llvm_mod = @llvm_mod - @llvm_mod = @main_mod - request_value do - accept const.value - end - LLVM.set_initializer global, @last - LLVM.set_global_constant global, true - @llvm_mod = old_llvm_mod - end - end + global = @main_mod.globals[global_name]#? || declare_const(const).not_nil! if @llvm_mod != @main_mod global = @llvm_mod.globals[global_name]? @@ -861,6 +885,36 @@ module Crystal false end + def declare_const(const, global_name = const.llvm_name) + return nil unless const.used + + global = @main_mod.globals.add(llvm_type(const.value.type), global_name) + + in_const_block(const.container) do + alloca_vars const.vars + + request_value do + accept const.value + end + + if LLVM.constant? @last + LLVM.set_initializer global, @last + LLVM.set_global_constant global, true + else + if const.value.type.passed_by_value? + @last = load @last + LLVM.set_initializer global, LLVM.undef(llvm_type(const.value.type)) + else + LLVM.set_initializer global, LLVM.null(type_of @last) + end + + store @last, global + end + end + + global + end + def visit(node : Generic) @last = type_id(node.type) false @@ -1363,7 +1417,7 @@ module Crystal a_fun = @main_mod.functions.add(name, arg_types, return_type) do |func| context.fun = func func.append_basic_block("entry") do |builder| - @builder = builder + @builder = wrap_builder builder yield func end end @@ -1668,24 +1722,21 @@ module Crystal def in_alloca_block old_block = insert_block - if context.in_const_block - position_at_end @main_alloca_block - else - position_at_end @alloca_block - end + position_at_end @alloca_block value = yield position_at_end old_block value end - def in_const_block(const_block_name, container) - old_position = insert_block + def in_const_block(container) old_llvm_mod = @llvm_mod + @llvm_mod = @main_mod + old_exception_handlers = @exception_handlers + @exception_handlers = nil with_cloned_context do context.fun = @main - context.in_const_block = true # "self" in a constant is the constant's container context.type = container @@ -1693,20 +1744,7 @@ module Crystal # Start with fresh variables context.vars = LLVMVars.new - @exception_handlers = nil - @llvm_mod = @main_mod - - const_block = new_block const_block_name - position_at_end const_block - yield - - new_const_block = insert_block - position_at_end @const_block - br const_block - @const_block = new_const_block - - position_at_end old_position end @llvm_mod = old_llvm_mod diff --git a/src/compiler/crystal/codegen/ast.cr b/src/compiler/crystal/codegen/ast.cr index 32550a571f42088067d83e08772470887a15c7df..3fd788b734e14157286bcb7fc1fa4db67f06a03c 100644 --- a/src/compiler/crystal/codegen/ast.cr +++ b/src/compiler/crystal/codegen/ast.cr @@ -22,10 +22,6 @@ module Crystal type?.try &.no_return? end - def needs_const_block? - true - end - def zero? false end @@ -52,14 +48,6 @@ module Crystal end end - {% for klass in %w(Nil Bool Number Char String Symbol) %} - class {{klass.id}}Literal - def needs_const_block? - false - end - end - {% end %} - class Assign def returns? value.returns? @@ -78,17 +66,6 @@ module Crystal end end - class Primitive - def needs_const_block? - case name - when :float32_infinity, :flaot64_infinity - false - else - true - end - end - end - class Return def returns? true diff --git a/src/compiler/crystal/codegen/context.cr b/src/compiler/crystal/codegen/context.cr index ec842eb9be117a925112411b97c561a624d2d415..d88485c6b2ff98d7066951d1239d6b4efcab9cdb 100644 --- a/src/compiler/crystal/codegen/context.cr +++ b/src/compiler/crystal/codegen/context.cr @@ -11,7 +11,6 @@ class Crystal::CodeGenVisitor < Crystal::Visitor property while_exit_block property! block property! block_context - property in_const_block property closure_vars property closure_type property closure_ptr @@ -20,7 +19,6 @@ class Crystal::CodeGenVisitor < Crystal::Visitor property closure_self def initialize(@fun, @type, @vars = LLVMVars.new) - @in_const_block = false @closure_skip_parent = false end @@ -51,7 +49,6 @@ class Crystal::CodeGenVisitor < Crystal::Visitor context.while_exit_block = @while_exit_block context.block = @block context.block_context = @block_context - context.in_const_block = @in_const_block context.closure_vars = @closure_vars context.closure_type = @closure_type context.closure_ptr = @closure_ptr diff --git a/src/compiler/crystal/codegen/fun.cr b/src/compiler/crystal/codegen/fun.cr index 4ddbdbf0785ab0d2374037de6909102bce76a35f..e6d5b648413968793e4c69854ad8ed5bb657ff92 100644 --- a/src/compiler/crystal/codegen/fun.cr +++ b/src/compiler/crystal/codegen/fun.cr @@ -46,7 +46,6 @@ class Crystal::CodeGenVisitor < Crystal::Visitor with_cloned_context do |old_context| context.type = self_type context.vars = LLVMVars.new - context.in_const_block = false @llvm_mod = fun_module @@ -55,7 +54,8 @@ class Crystal::CodeGenVisitor < Crystal::Visitor args = codegen_fun_signature(mangled_name, target_def, self_type, is_fun_literal, is_closure) - if !target_def.is_a?(External) || is_exported_fun + needs_body = !target_def.is_a?(External) || is_exported_fun + if needs_body emit_def_debug_metadata target_def if @debug new_entry_block @@ -122,6 +122,13 @@ class Crystal::CodeGenVisitor < Crystal::Visitor args.push Arg.new_with_type(block_arg.name, block_arg.type) end + # This is the case where we declared a fun that was not used and now we + # are defining its body. + if is_external && (existing_fun = @llvm_mod.functions[mangled_name]?) + context.fun = existing_fun + return args + end + if is_external llvm_args_types = args.map { |arg| llvm_c_type(arg.type) } llvm_return_type = llvm_c_type(target_def.type) diff --git a/src/compiler/crystal/normalizer.cr b/src/compiler/crystal/normalizer.cr index 000e4cfa23e01274cb9706ad7b121a4adc114571..3b894f10ff52a43c26d972be5451cfc7a1039205 100644 --- a/src/compiler/crystal/normalizer.cr +++ b/src/compiler/crystal/normalizer.cr @@ -123,23 +123,13 @@ module Crystal # # From: # - # /regex/ + # /regex/flags # # To: # - # ::CONST = /regex/ - # CONST + # Regex.new("regex", flags) def transform(node : RegexLiteral) - const_name = "#Regex_#{node.value}_#{node.modifiers}" - unless program.types[const_name]? - constructor = Call.new(Path.new(["Regex"], true), "new", [StringLiteral.new(node.value), NumberLiteral.new(node.modifiers, :i32)] of ASTNode) - program.types[const_name] = const = Const.new program, program, const_name, constructor, [program] of Type, program - @program.regexes << const - end - - path = Path.new([const_name], true) - path.location = node.location - path + Call.new(Path.new(["Regex"], true), "new", [StringLiteral.new(node.value), NumberLiteral.new(node.modifiers, :i32)] of ASTNode) end # Convert an interpolation to a concatenation with a StringIO: diff --git a/src/compiler/crystal/program.cr b/src/compiler/crystal/program.cr index 650ee7d889b2db615c518f8fda741feb31e3bf03..759f6f75b4c3d1ce41f5ae3764c69cffcc6798e6 100644 --- a/src/compiler/crystal/program.cr +++ b/src/compiler/crystal/program.cr @@ -11,7 +11,6 @@ module Crystal getter symbols getter global_vars - getter regexes property vars property literal_expander @@ -20,7 +19,6 @@ module Crystal @unions = {} of Array(Int32) => Type @funs = {} of Array(Int32) => Type - @regexes = [] of Const @types["Object"] = @object = NonGenericClassType.new self, self, "Object", nil @object.abstract = true @@ -54,10 +52,10 @@ module Crystal @float.abstract = true @types["Float32"] = @float32 = FloatType.new self, self, "Float32", @float, 4, 9 - @float32.types["INFINITY"] = Const.new self, @float32, "FLOAT_INFINITY", Primitive.new(:float32_infinity) + @float32.types["INFINITY"] = Const.new self, @float32, "INFINITY", Primitive.new(:float32_infinity) @types["Float64"] = @float64 = FloatType.new self, self, "Float64", @float, 8, 10 - @float64.types["INFINITY"] = Const.new self, @float64, "FLOAT_INFINITY", Primitive.new(:float64_infinity) + @float64.types["INFINITY"] = Const.new self, @float64, "INFINITY", Primitive.new(:float64_infinity) @types["Symbol"] = @symbol = SymbolType.new self, self, "Symbol", @value, 4 @types["Pointer"] = @pointer = PointerType.new self, self, "Pointer", @value, ["T"] diff --git a/src/compiler/crystal/type_inference.cr b/src/compiler/crystal/type_inference.cr index 7e4dae8929fb9b6fbd0dd457a9841281d0ccd84d..b839182fe440ebfcd83ab40b321340a517358ab2 100644 --- a/src/compiler/crystal/type_inference.cr +++ b/src/compiler/crystal/type_inference.cr @@ -444,9 +444,11 @@ module Crystal target.bind_to value - current_type.types[target.names.first] = Const.new(@mod, current_type, target.names.first, value, @types.dup, @scope) + const = Const.new(@mod, current_type, target.names.first, value, @types.dup, @scope) + current_type.types[target.names.first] = const node.type = @mod.nil + target.target_const = const end def type_assign(target : Global, value, node) @@ -1432,7 +1434,7 @@ module Crystal constant.default_value = NumberLiteral.new(counter, enum_base_type.kind) counter += 1 end - current_type.types[node.name] = CEnumType.new(@mod, current_type, node.name, enum_base_type, node.constants) + node.c_enum_type = current_type.types[node.name] = CEnumType.new(@mod, current_type, node.name, enum_base_type, node.constants) end false end @@ -1505,6 +1507,7 @@ module Crystal end node.target_const = type node.bind_to type.value + type.used = true when Type node.type = check_type_in_type_args(type.remove_alias_if_simple) when ASTNode diff --git a/src/compiler/crystal/type_inference/after_type_inference_transformer.cr b/src/compiler/crystal/type_inference/after_type_inference_transformer.cr index 7abdc5048f993ad54c82c535fb68baa08c2ef1e0..4a798f1f1877eab221fe8534986b728e80d7ad7f 100644 --- a/src/compiler/crystal/type_inference/after_type_inference_transformer.cr +++ b/src/compiler/crystal/type_inference/after_type_inference_transformer.cr @@ -8,12 +8,6 @@ module Crystal transformer = AfterTypeInferenceTransformer.new(self) node = node.transform(transformer) puts node if ENV["AFTER"]? == "1" - - # Make sure to transform regexes constants (see Normalizer#trnasform(Regex)) - regexes.each do |const| - const.value = const.value.transform(transformer) - end - node end end @@ -93,6 +87,15 @@ module Crystal end def transform(node : Assign) + target = node.target + + if target.is_a?(Path) + const = target.target_const.not_nil! + unless const.used + return node + end + end + node = super # We don't want to transform constant assignments into no return diff --git a/src/compiler/crystal/type_inference/ast.cr b/src/compiler/crystal/type_inference/ast.cr index 008ba108c1ee7a2ea824412baa075a5a01d9705a..58f30a865780742a590ccf38954a0b9b912e5a37 100644 --- a/src/compiler/crystal/type_inference/ast.cr +++ b/src/compiler/crystal/type_inference/ast.cr @@ -493,5 +493,12 @@ module Crystal class External property :dead @dead = false + + property :used + @used = false + end + + class EnumDef + property! c_enum_type end end diff --git a/src/compiler/crystal/type_inference/call.cr b/src/compiler/crystal/type_inference/call.cr index 0ed42998572c720a62945560b87709160e8cb9ee..4fa530505e556a9e1ce7846c21689cf17cf884e3 100644 --- a/src/compiler/crystal/type_inference/call.cr +++ b/src/compiler/crystal/type_inference/call.cr @@ -360,7 +360,7 @@ module Crystal def recalculate_lib_call(obj_type) old_target_defs = @target_defs - untyped_def = obj_type.lookup_first_def(name, false) #or + untyped_def = obj_type.lookup_first_def(name, false) raise "undefined fun '#{name}' for #{obj_type}" unless untyped_def check_args_length_match obj_type, untyped_def @@ -369,6 +369,8 @@ module Crystal check_fun_args_types_match obj_type, untyped_def + (untyped_def as External).used = true + untyped_defs = [untyped_def] @target_defs = untyped_defs diff --git a/src/compiler/crystal/types.cr b/src/compiler/crystal/types.cr index f43c62a5282cf500681b619db806539979a32508..40ac2900d5c14d3418c6c05e66a6ed936ecfcbb0 100644 --- a/src/compiler/crystal/types.cr +++ b/src/compiler/crystal/types.cr @@ -2564,9 +2564,11 @@ module Crystal getter scope_types getter scope property! vars + property used def initialize(program, container, name, @value, @scope_types = [] of Type, @scope = nil) super(program, container, name) + @used = false end def type_desc