X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index c1d1120..a6f6afa 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -21,6 +21,7 @@ import literal import semantize import platform import c_tools +private import annotation # Add compiling options redef class ToolContext @@ -329,7 +330,7 @@ class MakefileToolchain var outname = outfile(mainmodule) - var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd + var orig_dir = compile_dir.relpath(".") var outpath = orig_dir.join_path(outname).simplify_path var makename = makefile_name(mainmodule) var makepath = "{compile_dir}/{makename}" @@ -444,13 +445,13 @@ abstract class AbstractCompiler # The main module of the program currently compiled # Is assigned during the separate compilation - var mainmodule: MModule writable + var mainmodule: MModule is writable # The real main module of the program var realmainmodule: MModule # The modeulbuilder used to know the model and the AST - var modelbuilder: ModelBuilder protected writable + var modelbuilder: ModelBuilder is protected writable # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value @@ -479,7 +480,7 @@ abstract class AbstractCompiler fun new_visitor: VISITOR is abstract # Where global declaration are stored (the main .h) - var header: CodeWriter writable + var header: CodeWriter is writable # Provide a declaration that can be requested (before or latter) by a visitor fun provide_declaration(key: String, s: String) @@ -1017,10 +1018,10 @@ abstract class AbstractCompilerVisitor var compiler: COMPILER # The current visited AST node - var current_node: nullable ANode writable = null + var current_node: nullable ANode = null is writable # The current `Frame` - var frame: nullable Frame writable + var frame: nullable Frame is writable # Alias for self.compiler.mainmodule.object_type fun object_type: MClassType do return self.compiler.mainmodule.object_type @@ -1052,16 +1053,21 @@ abstract class AbstractCompilerVisitor if not initializers.is_empty then var recv = arguments.first - assert initializers.length == arguments.length - 1 else debug("expected {initializers.length}, got {arguments.length - 1}") var i = 1 for p in initializers do if p isa MMethod then - self.send(p, [recv, arguments[i]]) + var args = [recv] + for x in p.intro.msignature.mparameters do + args.add arguments[i] + i += 1 + end + self.send(p, args) else if p isa MAttribute then self.write_attribute(p, recv, arguments[i]) + i += 1 else abort - i += 1 end + assert i == arguments.length return self.send(callsite.mproperty, [recv]) end @@ -1149,12 +1155,22 @@ abstract class AbstractCompilerVisitor # Generate a super call from a method definition fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract + # Adapt the arguments of a method according to targetted `MMethodDef` fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract + # Unbox all the arguments of a method when implemented `extern` or `intern` + fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract + # Box or unbox a value to another type iff a C type conversion is needed # ENSURE: `result.mtype.ctype == mtype.ctype` fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract + # Box extern classes to be used in the generated code + fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract + + # Unbox extern classes to be used in extern code (legacy NI and FFI) + fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract + # Generate a polymorphic subtype test fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract @@ -1286,6 +1302,16 @@ abstract class AbstractCompilerVisitor return res end + # The difference with `new_var` is the C static type of the local variable + fun new_var_extern(mtype: MType): RuntimeVariable + do + mtype = self.anchor(mtype) + var name = self.get_name("var") + var res = new RuntimeVariable(name, mtype, mtype) + self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;") + return res + end + # Return a new uninitialized named runtime_variable fun new_named_var(mtype: MType, name: String): RuntimeVariable do @@ -1523,7 +1549,7 @@ abstract class AbstractRuntimeFunction # Non cached version of `c_name` protected fun build_c_name: String is abstract - protected var c_name_cache: nullable String writable = null + protected var c_name_cache: nullable String = null is writable # Implements a call of the runtime_function # May inline the body or generate a C function call @@ -1546,11 +1572,11 @@ class RuntimeVariable var mtype: MType # The current casted type of the variable (as known in Nit) - var mcasttype: MType writable + var mcasttype: MType is writable # If the variable exaclty a mcasttype? # false (usual value) means that the variable is a mcasttype or a subtype. - var is_exact: Bool writable = false + var is_exact: Bool = false is writable init(name: String, mtype: MType, mcasttype: MType) do @@ -1600,21 +1626,25 @@ class Frame var arguments: Array[RuntimeVariable] # The runtime_variable associated to the return (in a function) - var returnvar: nullable RuntimeVariable writable = null + var returnvar: nullable RuntimeVariable = null is writable # The label at the end of the property - var returnlabel: nullable String writable = null + var returnlabel: nullable String = null is writable end redef class MType # Return the C type associated to a given Nit static type fun ctype: String do return "val*" + # C type outside of the compiler code and in boxes + fun ctype_extern: String do return "val*" + + # Short name of the `ctype` to use in unions fun ctypename: String do return "val" # Return the name of the C structure associated to a Nit live type fun c_name: String is abstract - protected var c_name_cache: nullable String protected writable + protected var c_name_cache: nullable String is protected writable end redef class MClassType @@ -1641,13 +1671,20 @@ redef class MClassType return "char*" else if mclass.name == "NativeArray" then return "val*" - else if mclass.kind == extern_kind then - return "void*" else return "val*" end end + redef fun ctype_extern: String + do + if mclass.kind == extern_kind then + return "void*" + else + return ctype + end + end + redef fun ctypename: String do if mclass.name == "Int" then @@ -1663,8 +1700,6 @@ redef class MClassType else if mclass.name == "NativeArray" then #return "{self.arguments.first.ctype}*" return "val" - else if mclass.kind == extern_kind then - return "ptr" else return "val" end @@ -1862,6 +1897,18 @@ redef class AMethPropdef v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end + # Try special compilation + if mpropdef.is_intern then + if compile_intern_to_c(v, mpropdef, arguments) then return + else if mpropdef.is_extern then + if mpropdef.mproperty.is_init then + if compile_externinit_to_c(v, mpropdef, arguments) then return + else + if compile_externmeth_to_c(v, mpropdef, arguments) then return + end + end + + # Compile block if any var n_block = n_block if n_block != null then for i in [0..mpropdef.msignature.arity[ do @@ -1869,17 +1916,13 @@ redef class AMethPropdef v.assign(v.variable(variable), arguments[i+1]) end v.stmt(n_block) - else if mpropdef.is_intern then - compile_intern_to_c(v, mpropdef, arguments) - else if mpropdef.is_extern then - if mpropdef.mproperty.is_init then - compile_externinit_to_c(v, mpropdef, arguments) - else - compile_externmeth_to_c(v, mpropdef, arguments) - end - else - abort + return end + + # We have a problem + var cn = v.class_name_string(arguments.first) + v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});") + v.add_raw_abort end redef fun can_inline @@ -1892,7 +1935,7 @@ redef class AMethPropdef return false end - fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var pname = mpropdef.mproperty.name var cname = mpropdef.mclassdef.mclass.name @@ -1904,243 +1947,245 @@ redef class AMethPropdef end if pname != "==" and pname != "!=" then v.adapt_signature(mpropdef, arguments) + v.unbox_signature_extern(mpropdef, arguments) end if cname == "Int" then if pname == "output" then v.add("printf(\"%ld\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(arguments.first) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "%" then v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) - return + return true else if pname == "lshift" then v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) - return + return true else if pname == "rshift" then v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_f" then v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Char" then if pname == "output" then v.add("printf(\"%c\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "successor" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "predecessor" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Bool" then if pname == "output" then v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true end else if cname == "Float" then if pname == "output" then v.add("printf(\"%f\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(double){arguments.first}", ret.as(not null))) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "succ" then v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return + return true else if pname == "prec" then v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "NativeString" then if pname == "[]" then v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) - return + return true else if pname == "[]=" then v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") - return + return true else if pname == "copy_to" then v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") - return + return true else if pname == "atoi" then v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null))) - return + return true else if pname == "init" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true end else if cname == "NativeArray" then v.native_array_def(pname, ret, arguments) - return + return true end if pname == "exit" then v.add("exit({arguments[1]});") - return + return true else if pname == "sys" then v.ret(v.new_expr("glob_sys", ret.as(not null))) - return + return true else if pname == "calloc_string" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true else if pname == "calloc_array" then v.calloc_array(ret.as(not null), arguments) - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "is_same_type" then v.ret(v.is_same_type_test(arguments[0], arguments[1])) - return + return true else if pname == "is_same_instance" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "output_class_name" then var nat = v.class_name_string(arguments.first) v.add("printf(\"%s\\n\", {nat});") - return + return true else if pname == "native_class_name" then var nat = v.class_name_string(arguments.first) v.ret(v.new_expr("(char*){nat}", ret.as(not null))) - return + return true else if pname == "force_garbage_collection" then v.add("nit_gcollect();") - return + return true else if pname == "native_argc" then v.ret(v.new_expr("glob_argc", ret.as(not null))) - return + return true else if pname == "native_argv" then v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null))) - return + return true end - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") - debug("Not implemented {mpropdef}") + return false end - fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern method + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + return false end - externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename v.add_extern(file) @@ -2149,40 +2194,48 @@ redef class AMethPropdef var ret = mpropdef.msignature.return_mtype if ret != null then ret = v.resolve_for(ret, arguments.first) - res = v.new_var(ret) + res = v.new_var_extern(ret) end v.adapt_signature(mpropdef, arguments) + v.unbox_signature_extern(mpropdef, arguments) if res == null then v.add("{externname}({arguments.join(", ")});") else v.add("{res} = {externname}({arguments.join(", ")});") + res = v.box_extern(res, ret.as(not null)) v.ret(res) end + return true end - fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern factory + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + return false end - externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename v.add_extern(file) end v.adapt_signature(mpropdef, arguments) + v.unbox_signature_extern(mpropdef, arguments) var ret = arguments.first.mtype - var res = v.new_var(ret) + var res = v.new_var_extern(ret) arguments.shift v.add("{res} = {externname}({arguments.join(", ")});") + res = v.box_extern(res, ret) v.ret(res) + return true end end @@ -2271,33 +2324,12 @@ redef class AClassdef private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do if mpropdef == self.mfree_init then - if mpropdef.mproperty.is_root_init then - assert self.super_inits == null - assert arguments.length == 1 - if not mpropdef.is_intro then - v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) - end - return - end - - var super_inits = self.super_inits - if super_inits != null then - var args_of_super = arguments - if arguments.length > 1 then args_of_super = [arguments.first] - for su in super_inits do - v.send(su, args_of_super) - end - end - - var recv = arguments.first - var i = 1 - # Collect undefined attributes - for npropdef in self.n_propdefs do - if npropdef isa AAttrPropdef and npropdef.n_expr == null and not npropdef.noinit then - v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i]) - i += 1 - end + assert mpropdef.mproperty.is_root_init + assert arguments.length == 1 + if not mpropdef.is_intro then + v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end + return else abort end @@ -2569,6 +2601,12 @@ redef class AForExpr v.compile_callsite(next_meth, [it]) v.add("\}") v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + + var method_finish = self.method_finish + if method_finish != null then + # TODO: Find a way to call this also in long escape (e.g. return) + v.compile_callsite(method_finish, [it]) + end end end @@ -2875,7 +2913,7 @@ redef class ANewExpr return v.native_array_instance(elttype, l) else if ctype == "val*" then recv = v.init_instance(mtype) - else if ctype == "void*" then + else if ctype == "char*" then recv = v.new_expr("NULL/*special!*/", mtype) else recv = v.new_expr("({ctype})0/*special!*/", mtype)