From: Jean Privat Date: Mon, 8 Jun 2015 10:05:14 +0000 (-0400) Subject: Merge: More keep going X-Git-Tag: v0.7.6~61 X-Git-Url: http://nitlanguage.org?hp=-c Merge: More keep going This improve the robustness of tools when given --keep-going. Tools like nitpick, that have --keep-going by default, are more robust and collect more errors. Moreover, the compiler can now compile simple programs with instructions that fail during the typing phase (most errors like unknown method or bad type). These instructions are compiled with a run time error instead so the program is still expected to behave in an reliable way. ~~~nit print 1 fail now print 2 ~~~ ~~~sh $ nitc kg.nit --keep-going kg.nit:2,1--4: Error: method or variable `fail` unknown in `Sys`. $ ./kg 1 Runtime error: FATAL: bad statement executed. (kg.nit:1) ~~~ One usage would be to force `c_src` to compile things even if it lags behind. Pull-Request: #1440 Reviewed-by: Lucas Bajolet Reviewed-by: Alexis Laferrière Reviewed-by: Romain Chanoir --- d0279f4e1414d9a12b2d5320115dbc95b1fb88f0 diff --combined src/compiler/abstract_compiler.nit index a095201,b69a049..9d3b0f3 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@@ -63,8 -63,8 +63,8 @@@ redef class ToolContex var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics") # --isset-checks-metrics var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics") - # --stacktrace - var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace") + # --no-stacktrace + var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace") # --no-gcc-directives var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive") # --release @@@ -76,7 -76,7 +76,7 @@@ self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening) self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all) self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics) - self.option_context.add_option(self.opt_stacktrace) + self.option_context.add_option(self.opt_no_stacktrace) self.option_context.add_option(self.opt_no_gcc_directive) self.option_context.add_option(self.opt_release) self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files) @@@ -88,6 -88,17 +88,6 @@@ do super - var st = opt_stacktrace.value - if st == "none" or st == "libunwind" or st == "nitstack" then - # Fine, do nothing - else if st == "auto" or st == null then - # Default is nitstack - opt_stacktrace.value = "nitstack" - else - print "Option Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`." - exit(1) - end - if opt_output.value != null and opt_dir.value != null then print "Option Error: cannot use both --dir and --output" exit(1) @@@ -104,12 -115,15 +104,12 @@@ end redef class ModelBuilder - # The compilation directory - var compile_dir: String - # Simple indirection to `Toolchain::write_and_make` protected fun write_and_make(compiler: AbstractCompiler) do var platform = compiler.target_platform var toolchain = platform.toolchain(toolcontext, compiler) - compile_dir = toolchain.compile_dir + compiler.toolchain = toolchain toolchain.write_and_make end end @@@ -131,21 -145,14 +131,21 @@@ class Toolchai # Compiler of the target program var compiler: AbstractCompiler - # Directory where to generate all C files - fun compile_dir: String + # Directory where to generate all files + # + # The option `--compile_dir` change this directory. + fun root_compile_dir: String do var compile_dir = toolcontext.opt_compile_dir.value - if compile_dir == null then compile_dir = ".nit_compile" + if compile_dir == null then compile_dir = "nit_compile" return compile_dir end + # Directory where to generate all C files + # + # By default it is `root_compile_dir` but some platform may require that it is a subdirectory. + fun compile_dir: String do return root_compile_dir + # Write all C files and compile them fun write_and_make is abstract end @@@ -158,16 -165,12 +158,16 @@@ class MakefileToolchai do var compile_dir = compile_dir + # Remove the compilation directory unless explicitly set + var auto_remove = toolcontext.opt_compile_dir.value == null + # Generate the .h and .c files # A single C file regroups many compiled rumtime functions # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files var time0 = get_time self.toolcontext.info("*** WRITING C ***", 1) + root_compile_dir.mkdir compile_dir.mkdir var cfiles = new Array[String] @@@ -189,10 -192,6 +189,10 @@@ compile_c_code(compile_dir) + if auto_remove then + sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'") + end + time1 = get_time self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2) end @@@ -201,7 -200,7 +201,7 @@@ fun write_files(compile_dir: String, cfiles: Array[String]) do var platform = compiler.target_platform - if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings + if platform.supports_libunwind then compiler.build_c_to_nit_bindings var cc_opt_with_libgc = "-DWITH_LIBGC" if not platform.supports_libgc then cc_opt_with_libgc = "" @@@ -328,7 -327,7 +328,7 @@@ var outpath = real_outpath.escape_to_mk if outpath != real_outpath then # If the name is crazy and need escaping, we will do an indirection - # 1. generate the binary in the .nit_compile dir under an escaped name + # 1. generate the binary in the nit_compile dir under an escaped name # 2. copy the binary at the right place in the `all` goal. outpath = mainmodule.c_name end @@@ -344,50 -343,25 +344,50 @@@ makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n") - var ost = toolcontext.opt_stacktrace.value - if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n") + makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n" + if platform.supports_libunwind then + if toolcontext.opt_no_stacktrace.value then + makefile.write "NO_STACKTRACE=True" + else + makefile.write "NO_STACKTRACE= # Set to `True` to enable" + end + end # Dynamic adaptations # While `platform` enable complex toolchains, they are statically applied # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself + makefile.write "\n\n" # Check and adapt the targeted system makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n") - makefile.write("ifeq ($(uname_S),Darwin)\n") - # remove -lunwind since it is already included on macosx - makefile.write("\tNEED_LIBUNWIND :=\n") - makefile.write("endif\n\n") # Check and adapt for the compiler used # clang need an additionnal `-Qunused-arguments` makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n") - makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n") + if platform.supports_libunwind then + makefile.write """ +ifneq ($(NO_STACKTRACE), True) + # Check and include lib-unwind in a portable way + ifneq ($(uname_S),Darwin) + # already included on macosx, but need to get the correct flags in other supported platforms. + ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0) + LDLIBS += `pkg-config --libs libunwind` + CFLAGS += `pkg-config --cflags libunwind` + else + $(warning "[_] stack-traces disabled. Please install libunwind-dev.") + CFLAGS += -D NO_STACKTRACE + endif + endif +else + # Stacktraces disabled + CFLAGS += -D NO_STACKTRACE +endif + +""" + else + makefile.write("CFLAGS += -D NO_STACKTRACE\n\n") + end makefile.write("all: {outpath}\n") if outpath != real_outpath then @@@ -522,11 -496,6 +522,11 @@@ abstract class AbstractCompile # The modelbuilder used to know the model and the AST var modelbuilder: ModelBuilder is protected writable + # The associated toolchain + # + # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required. + var toolchain: Toolchain is noinit + # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value @@@ -590,7 -559,7 +590,7 @@@ # Binds the generated C function names to Nit function names fun build_c_to_nit_bindings do - var compile_dir = modelbuilder.compile_dir + var compile_dir = toolchain.compile_dir var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c") stream.write("#include \n") @@@ -637,7 -606,6 +637,7 @@@ self.header.add_decl("#include ") self.header.add_decl("#include \n") self.header.add_decl("#include \n") + self.header.add_decl("#include \n") self.header.add_decl("#include \"gc_chooser.h\"") self.header.add_decl("#ifdef ANDROID") self.header.add_decl(" #include ") @@@ -745,16 -713,19 +745,16 @@@ extern void nitni_global_ref_decr( stru do var v = self.new_visitor v.add_decl("#include ") - var ost = modelbuilder.toolcontext.opt_stacktrace.value var platform = target_platform - if not platform.supports_libunwind then ost = "none" - var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value - if ost == "nitstack" or ost == "libunwind" then + if platform.supports_libunwind then + v.add_decl("#ifndef NO_STACKTRACE") v.add_decl("#define UNW_LOCAL_ONLY") v.add_decl("#include ") - if ost == "nitstack" then - v.add_decl("#include \"c_functions_hash.h\"") - end + v.add_decl("#include \"c_functions_hash.h\"") + v.add_decl("#endif") end v.add_decl("int glob_argc;") v.add_decl("char **glob_argv;") @@@ -788,8 -759,7 +788,8 @@@ end v.add_decl("static void show_backtrace(void) \{") - if ost == "nitstack" or ost == "libunwind" then + if platform.supports_libunwind then + v.add_decl("#ifndef NO_STACKTRACE") v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");") v.add_decl("unw_cursor_t cursor;") v.add_decl("if(opt==NULL)\{") @@@ -803,17 -773,20 +803,17 @@@ v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");") v.add_decl("while (unw_step(&cursor) > 0) \{") v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);") - if ost == "nitstack" then v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));") v.add_decl(" if (recv != NULL)\{") v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);") v.add_decl(" \}else\{") v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);") v.add_decl(" \}") - else - v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);") - end v.add_decl("\}") v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");") v.add_decl("free(procname);") v.add_decl("\}") + v.add_decl("#endif /* NO_STACKTRACE */") end v.add_decl("\}") @@@ -1482,14 -1455,6 +1482,14 @@@ abstract class AbstractCompilerVisito return res end + # Generate a byte value + fun byte_instance(value: Byte): RuntimeVariable + do + var t = mmodule.byte_type + var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t) + return res + end + # Generate a char value fun char_instance(value: Char): RuntimeVariable do @@@ -1672,8 -1637,9 +1672,9 @@@ if nexpr == null then return if nexpr.mtype == null and not nexpr.is_typed then # Untyped expression. - # Might mean dead code - # So just return + # Might mean dead code or invalid code + # so aborts + add_abort("FATAL: bad statement executed.") return end @@@ -1697,8 -1663,10 +1698,10 @@@ do if nexpr.mtype == null then # Untyped expression. - # Might mean dead code - # so return a placebo result + # Might mean dead code or invalid code. + # so aborts + add_abort("FATAL: bad expression executed.") + # and return a placebo result to please the C compiler if mtype == null then mtype = compiler.mainmodule.object_type return new_var(mtype) end @@@ -1870,13 -1838,11 +1873,13 @@@ redef class MClassTyp else if mclass.name == "Bool" then return "short int" else if mclass.name == "Char" then - return "char" + return "uint32_t" else if mclass.name == "Float" then return "double" + else if mclass.name == "Byte" then + return "unsigned char" else if mclass.name == "NativeString" then - return "char*" + return "unsigned char*" else if mclass.name == "NativeArray" then return "val*" else @@@ -1905,8 -1871,6 +1908,8 @@@ return "c" else if mclass.name == "Float" then return "d" + else if mclass.name == "Byte" then + return "b" else if mclass.name == "NativeString" then return "str" else if mclass.name == "NativeArray" then @@@ -2137,16 -2101,13 +2140,16 @@@ redef class AMethPropde else if pname == "to_f" then v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true else if pname == "ascii" then - v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) return true end else if cname == "Char" then if pname == "output" then - v.add("printf(\"%c\", {arguments.first});") + v.add("printf(\"%c\", ((unsigned char){arguments.first}));") return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) @@@ -2180,70 -2141,7 +2183,70 @@@ v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) return true else if pname == "ascii" then - v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + end + else if cname == "Byte" then + if pname == "output" then + v.add("printf(\"%x\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "lshift" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == "rshift" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + 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 true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "ascii" then + v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) return true end else if cname == "Bool" then @@@ -2314,16 -2212,13 +2317,16 @@@ else if pname == "to_i" then v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true end else if cname == "NativeString" then if pname == "[]" then - v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) + v.ret(v.new_expr("(uint32_t){arguments[0]}[{arguments[1]}]", ret.as(not null))) return true else if pname == "[]=" then - v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") + v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};") return true else if pname == "copy_to" then v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") @@@ -2335,7 -2230,7 +2338,7 @@@ v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) return true else if pname == "new" then - v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) + v.ret(v.new_expr("(unsigned char*)nit_alloc({arguments[1]})", ret.as(not null))) return true end else if cname == "NativeArray" then @@@ -2349,7 -2244,7 +2352,7 @@@ v.ret(v.new_expr("glob_sys", ret.as(not null))) return true else if pname == "calloc_string" then - v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) + v.ret(v.new_expr("(unsigned char*)nit_alloc({arguments[1]})", ret.as(not null))) return true else if pname == "calloc_array" then v.calloc_array(ret.as(not null), arguments) @@@ -2893,10 -2788,6 +2896,10 @@@ redef class AIntExp redef fun expr(v) do return v.int_instance(self.value.as(not null)) end +redef class AByteExpr + redef fun expr(v) do return v.byte_instance(self.value.as(not null)) +end + redef class AFloatExpr redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float end diff --combined src/compiler/separate_compiler.nit index f50a2a7,dc24b7c..e07f125 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@@ -252,7 -252,7 +252,7 @@@ class SeparateCompile do # Collect all bas box class # FIXME: this is not completely fine with a separate compilation scheme - for classname in ["Int", "Bool", "Char", "Float", "NativeString", "Pointer"] do + for classname in ["Int", "Bool", "Byte", "Char", "Float", "NativeString", "Pointer"] do var classes = self.mainmodule.model.get_mclasses_by_name(classname) if classes == null then continue assert classes.length == 1 else print classes.join(", ") @@@ -625,6 -625,7 +625,7 @@@ for cd in mmodule.mclassdefs do for pd in cd.mpropdefs do if not pd isa MMethodDef then continue + if pd.msignature == null then continue # Skip broken method var rta = runtime_type_analysis if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue #print "compile {pd} @ {cd} @ {mmodule}" @@@ -768,8 -769,7 +769,8 @@@ end v.add_decl("\},") else - v.add_decl("0, \{\}, /*DEAD TYPE*/") + # Use -1 to indicate dead type, the info is used by --hardening + v.add_decl("-1, \{\}, /*DEAD TYPE*/") end v.add_decl("\};") end @@@ -1037,7 -1037,7 +1038,7 @@@ v.add("if({t} == NULL) \{") v.add_abort("type null") v.add("\}") - v.add("if({t}->table_size == 0) \{") + v.add("if({t}->table_size < 0) \{") v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);") v.add_abort("type dead") v.add("\}") @@@ -1193,7 -1193,7 +1194,7 @@@ class SeparateCompilerVisito if mtype.name == "Int" then return self.new_expr("(long)({value})>>2", mtype) else if mtype.name == "Char" then - return self.new_expr("(char)((long)({value})>>2)", mtype) + return self.new_expr("(uint32_t)((long)({value})>>2)", mtype) else if mtype.name == "Bool" then return self.new_expr("(short int)((long)({value})>>2)", mtype) else diff --combined src/rapid_type_analysis.nit index e27a98e,734a76a..d81d1c7 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@@ -213,18 -213,20 +213,21 @@@ class RapidTypeAnalysi force_alive("Float") force_alive("Char") force_alive("Pointer") + force_alive("Byte") while not todo.is_empty do var mmethoddef = todo.shift var mmeth = mmethoddef.mproperty + var msignature = mmethoddef.msignature + if msignature == null then continue # Skip broken method + #print "# visit {mmethoddef}" var v = new RapidTypeVisitor(self, mmethoddef.mclassdef.bound_mtype, mmethoddef) - var vararg_rank = mmethoddef.msignature.vararg_rank + var vararg_rank = msignature.vararg_rank if vararg_rank > -1 then var node = self.modelbuilder.mpropdef2node(mmethoddef) - var elttype = mmethoddef.msignature.mparameters[vararg_rank].mtype + var elttype = msignature.mparameters[vararg_rank].mtype #elttype = elttype.anchor_to(self.mainmodule, v.receiver) var vararg = self.mainmodule.array_type(elttype) v.add_type(vararg) @@@ -234,7 -236,7 +237,7 @@@ end # TODO? new_msignature - var sig = mmethoddef.msignature.as(not null) + var sig = msignature var osig = mmeth.intro.msignature.as(not null) for i in [0..sig.arity[ do var origtype = osig.mparameters[i].mtype @@@ -255,7 -257,7 +258,7 @@@ continue else if mmethoddef.constant_value != null then # Make the return type live - v.add_type(mmethoddef.msignature.return_mtype.as(MClassType)) + v.add_type(msignature.return_mtype.as(MClassType)) continue else if npropdef == null then abort @@@ -275,7 -277,7 +278,7 @@@ if mmethoddef.is_intern or mmethoddef.is_extern then # UGLY: We force the "instantation" of the concrete return type if any - var ret = mmethoddef.msignature.return_mtype + var ret = msignature.return_mtype if ret != null and ret isa MClassType and ret.mclass.kind != abstract_kind and ret.mclass.kind != interface_kind then v.add_type(ret) end @@@ -297,10 -299,10 +300,10 @@@ if not ot.can_resolve_for(t, t, mainmodule) then continue var rt = ot.anchor_to(mainmodule, t) if live_types.has(rt) then continue + if not check_depth(rt) then continue #print "{ot}/{t} -> {rt}" live_types.add(rt) todo_types.add(rt) - check_depth(rt) end end #print "MType {live_types.length}: {live_types.join(", ")}" @@@ -318,12 -320,14 +321,14 @@@ #print "cast MType {live_cast_types.length}: {live_cast_types.join(", ")}" end - private fun check_depth(mtype: MClassType) + private fun check_depth(mtype: MClassType): Bool do var d = mtype.length if d > 255 then self.modelbuilder.toolcontext.fatal_error(null, "Fatal Error: limitation in the rapidtype analysis engine: a type depth of {d} is too important, the problematic type is `{mtype}`.") + return false end + return true end fun add_new(recv: MClassType, mtype: MClassType) @@@ -450,10 -454,14 +455,14 @@@ class RapidTypeVisito redef fun visit(n) do - n.accept_rapid_type_visitor(self) if n isa AExpr then - var implicit_cast_to = n.implicit_cast_to - if implicit_cast_to != null then self.add_cast_type(implicit_cast_to) + if n.mtype != null or n.is_typed then + n.accept_rapid_type_visitor(self) + var implicit_cast_to = n.implicit_cast_to + if implicit_cast_to != null then self.add_cast_type(implicit_cast_to) + end + else + n.accept_rapid_type_visitor(self) end # RTA does not enter in AAnnotations @@@ -517,13 -525,6 +526,13 @@@ redef class AIntExp end end +redef class AByteExpr + redef fun accept_rapid_type_visitor(v) + do + v.add_type(self.mtype.as(MClassType)) + end +end + redef class AFloatExpr redef fun accept_rapid_type_visitor(v) do @@@ -567,7 -568,7 +576,7 @@@ redef class ASuperstringExp redef fun accept_rapid_type_visitor(v) do var mmodule = v.analysis.mainmodule - var object_type = mmodule.object_type + var object_type = mmodule.string_type var arraytype = mmodule.array_type(object_type) v.add_type(arraytype) var nattype = mmodule.native_array_type(object_type) diff --combined src/semantize/typing.nit index 44ed320,020ce3b..4aad2d2 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@@ -301,9 -301,15 +301,9 @@@ private class TypeVisito #debug("recv: {recvtype} (aka {unsafe_type})") if recvtype isa MNullType then - # `null` only accepts some methods of object. - if name == "==" or name == "!=" or name == "is_same_instance" then - var objclass = get_mclass(node, "Object") - if objclass == null then return null # Forward error - unsafe_type = objclass.mclass_type - else - self.error(node, "Error: method `{name}` called on `null`.") - return null - end + var objclass = get_mclass(node, "Object") + if objclass == null then return null # Forward error + unsafe_type = objclass.mclass_type end var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) @@@ -325,14 -331,6 +325,14 @@@ assert mproperty isa MMethod + # `null` only accepts some methods of object. + if recvtype isa MNullType and not mproperty.is_null_safe then + self.error(node, "Error: method `{name}` called on `null`.") + return null + else if unsafe_type isa MNullableType and not mproperty.is_null_safe then + modelbuilder.advice(node, "call-on-nullable", "Warning: method call on a nullable receiver `{recvtype}`.") + end + if is_toplevel_context and recv_is_self and not mproperty.is_toplevel then error(node, "Error: `{name}` is not a top-level method, thus need a receiver.") end @@@ -960,7 -958,7 +960,7 @@@ redef class AVarReassignExp v.set_variable(self, variable, rettype) - self.is_typed = true + self.is_typed = rettype != null end end @@@ -1006,9 -1004,11 +1006,11 @@@ redef class AReturnExp else v.visit_expr(nexpr) v.error(nexpr, "Error: `return` with value in a procedure.") + return end else if ret_type != null then v.error(self, "Error: `return` without value in a function.") + return end self.is_typed = true end @@@ -1344,15 -1344,6 +1346,15 @@@ redef class AIntExp end end +redef class AByteExpr + redef fun accept_typing(v) + do + var mclass = v.get_mclass(self, "Byte") + if mclass == null then return # Forward error + self.mtype = mclass.mclass_type + end +end + redef class AFloatExpr redef fun accept_typing(v) do @@@ -2061,7 -2052,7 +2063,7 @@@ redef class AAttrAssignExp var mtype = self.attr_type v.visit_expr_subtype(self.n_value, mtype) - self.is_typed = true + self.is_typed = mtype != null end end @@@ -2072,9 -2063,9 +2074,9 @@@ redef class AAttrReassignExp var mtype = self.attr_type if mtype == null then return # Skip error - self.resolve_reassignment(v, mtype, mtype) + var rettype = self.resolve_reassignment(v, mtype, mtype) - self.is_typed = true + self.is_typed = rettype != null end end