From: Jean Privat Date: Wed, 15 Oct 2014 20:15:47 +0000 (-0400) Subject: Merge: transform loops X-Git-Tag: v0.6.10~30 X-Git-Url: http://nitlanguage.org?hp=-c Merge: transform loops In order to activate more optimizations and simplify code generators and analysis, the `while` and `for` loops must be transformed into simpler forms (in fact in complex forms but with simpler elements) However, to do such a transformation, the escaping mechanism (break/continue/goto) must be rewrote. Previously, for each loop control structure (loop for, while), a single EscapeMark was generated and breaks and continues are associated to the same mark, even if they branch in distinct points (so each escapemark had 2 branching mechanisms) Now, with the first commits, two escapemarks are generated for each loop control structure, and breaks and continues are associated to a different one. The advantage is that each mark only have a single branching mechanism and that once associated to their mark, there is no need to distinguish break and continue (both become simple gotos). The transformations of loops can be then implemented. The `while` ~~~ while cond do block ~~~ is transformed into ~~~ loop if cond then block else break ~~~ and `for` ~~~ for i in col do block ~~~ is transformed into ~~~ var it = col.iterator loop if it.is_ok then var i = it.item do block # ^ `continue` in the block it attached to the `do` # this is why the transformation of escape-marks where needed in fact. # and break is associated to the main loop it.next else break end end it.finish ~~~ Note that these transformations are basically what is currently implemented in rta, niti and nitg. Except that, now with a single transformation point, those three workers does not need to do their own transformation and basically can just rely on the one done on the AST level. Pull-Request: #818 Reviewed-by: Lucas Bajolet --- 380d22070188dd1a255dbc3e33a0f2881242aab2 diff --combined src/astbuilder.nit index 3dae82c,fd2e22e..630c1c0 --- a/src/astbuilder.nit +++ b/src/astbuilder.nit @@@ -53,6 -53,12 +53,12 @@@ class ASTBuilde return new ABlockExpr.make end + # Make a new, empty, loop of statements + fun make_loop: ALoopExpr + do + return new ALoopExpr.make + end + # Make a new variable read fun make_var_read(variable: Variable, mtype: MType): AVarExpr do @@@ -84,6 -90,12 +90,12 @@@ return new ADoExpr.make end + # Make a new break for a given escapemark + fun make_break(escapemark: EscapeMark): ABreakExpr + do + return new ABreakExpr.make(escapemark) + end + # Make a new condinionnal # `mtype` is the return type of the whole if, in case of a ternary operator. fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr @@@ -142,6 -154,7 +154,7 @@@ redef class AExp # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3 fun add(expr: AExpr) do + print "add not implemented in {inspect}" abort end end @@@ -172,29 -185,54 +185,54 @@@ redef class ABlockExp end end + redef class ALoopExpr + private init make + do + _n_kwloop = new TKwloop + self.is_typed = true + n_block = new ABlockExpr + n_block.is_typed = true + end + + redef fun add(expr: AExpr) + do + n_block.add expr + end + end + redef class ADoExpr private init make do _n_kwdo = new TKwdo - escapemark = new EscapeMark(null, false) + self.is_typed = true + n_block = new ABlockExpr + n_block.is_typed = true end # Make a new break expression of the given do fun make_break: ABreakExpr do - var escapemark = self.escapemark + var escapemark = self.break_mark if escapemark == null then - escapemark = new EscapeMark(null, false) - self.escapemark = escapemark + escapemark = new EscapeMark(null) + self.break_mark = escapemark end return new ABreakExpr.make(escapemark) end + + redef fun add(expr: AExpr) + do + n_block.add expr + end end redef class ABreakExpr private init make(escapemark: EscapeMark) do + _n_kwbreak = new TKwbreak self.escapemark = escapemark + escapemark.escapes.add self + self.is_typed = true end end @@@ -251,6 -289,7 +289,6 @@@ redef class ACallExp if args != null then self.n_args.n_exprs.add_all(args) end - var mtype = recv.mtype.as(not null) self.callsite = callsite self.mtype = callsite.msignature.return_mtype self.is_typed = true diff --combined src/compiler/abstract_compiler.nit index 0a11d32,2897ed9..f111e0d --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@@ -22,48 -22,45 +22,46 @@@ import semantiz import platform import c_tools private import annotation +import mixin # Add compiling options redef class ToolContext # --output - var opt_output: OptionString = new OptionString("Output file", "-o", "--output") + var opt_output = new OptionString("Output file", "-o", "--output") # --dir - var opt_dir: OptionString = new OptionString("Output directory", "--dir") + var opt_dir = new OptionString("Output directory", "--dir") # --no-cc - var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc") + var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc") # --no-main - var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main") + var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main") # --cc-paths - var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path") + var opt_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path") # --make-flags - var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags") + var opt_make_flags = new OptionString("Additional options to make", "--make-flags") # --compile-dir - var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir") + var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir") # --hardening - var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") + var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") - # --no-shortcut-range - var opt_no_shortcut_range = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range") # --no-check-covariance - var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") + var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") # --no-check-attr-isset - var opt_no_check_attr_isset: OptionBool = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset") + var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset") # --no-check-assert - var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert") + var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert") # --no-check-autocast - var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") + var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") # --no-check-null - var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null") + var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null") # --no-check-all - var opt_no_check_all: OptionBool = new OptionBool("Disable all tests (dangerous)", "--no-check-all") + var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all") # --typing-test-metrics - var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics") + var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics") # --invocation-metrics - var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics") + 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: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--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: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace") + var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace") # --no-gcc-directives var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive") # --release @@@ -72,7 -69,7 +70,7 @@@ redef init do super - 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.opt_no_shortcut_range) + 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) @@@ -182,6 -179,7 +180,6 @@@ class MakefileToolchai do gather_cc_paths - var mainmodule = compiler.mainmodule var compile_dir = compile_dir # Generate the .h and .c files @@@ -229,6 -227,7 +227,6 @@@ compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h" # FFI - var m2m = toolcontext.modelbuilder.mmodule2nmodule for m in compiler.mainmodule.in_importation.greaters do compiler.finalize_ffi_for_module(m) end @@@ -309,16 -308,7 +307,16 @@@ fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk" - fun default_outname(mainmodule: MModule): String do return mainmodule.name + fun default_outname(mainmodule: MModule): String + do + # Search a non fictive module + var res = mainmodule.name + while mainmodule.is_fictive do + mainmodule = mainmodule.in_importation.direct_greaters.first + res = mainmodule.name + end + return res + end # Combine options and platform informations to get the final path of the outfile fun outfile(mainmodule: MModule): String @@@ -350,6 -340,7 +348,6 @@@ end var linker_options = new HashSet[String] - var m2m = toolcontext.modelbuilder.mmodule2nmodule for m in mainmodule.in_importation.greaters do var libs = m.collect_linker_libs if libs != null then linker_options.add_all(libs) @@@ -457,7 -448,7 +455,7 @@@ abstract class AbstractCompile # The real main module of the program var realmainmodule: MModule - # The modeulbuilder used to know the model and the AST + # The modelbuilder used to know the model and the AST var modelbuilder: ModelBuilder is protected writable # Is hardening asked? (see --hardening) @@@ -481,7 -472,7 +479,7 @@@ # The list of all associated files # Used to generate .c files - var files: List[CodeFile] = new List[CodeFile] + var files = new List[CodeFile] # Initialize a visitor specific for a compiler engine fun new_visitor: VISITOR is abstract @@@ -548,6 -539,8 +546,6 @@@ # Compile C headers # This method call compile_header_strucs method that has to be refined fun compile_header do - var v = self.header - var toolctx = modelbuilder.toolcontext self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") @@@ -896,7 -889,6 +894,7 @@@ extern void nitni_global_ref_decr( stru var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do + if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue var n = self.modelbuilder.mclassdef2nclassdef[cd] for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then @@@ -912,7 -904,6 +910,7 @@@ var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do + if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue var n = self.modelbuilder.mclassdef2nclassdef[cd] for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then @@@ -1051,7 -1042,7 +1049,7 @@@ abstract class AbstractCompilerVisito fun get_property(name: String, recv: MType): MMethod do assert recv isa MClassType - return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule) + return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule) end fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable @@@ -1224,7 -1215,7 +1222,7 @@@ # Checks - # Add a check and an abort for a null reciever if needed + # Add a check and an abort for a null receiver if needed fun check_recv_notnull(recv: RuntimeVariable) do if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return @@@ -1239,7 -1230,7 +1237,7 @@@ # Names handling - private var names: HashSet[String] = new HashSet[String] + private var names = new HashSet[String] private var last: Int = 0 # Return a new name based on `s` and unique in the visitor @@@ -1273,6 -1264,14 +1271,14 @@@ return name end + # Insert a C label for associated with an escapemark + fun add_escape_label(e: nullable EscapeMark) + do + if e == null then return + if e.escapes.is_empty then return + add("BREAK_{escapemark_name(e)}: (void)0;") + end + private var escapemark_names = new HashMap[EscapeMark, String] # Return a "const char*" variable associated to the classname of the dynamic type of an object @@@ -1281,7 -1280,7 +1287,7 @@@ # Variables handling - protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable] + protected var variables = new HashMap[Variable, RuntimeVariable] # Return the local runtime_variable associated to a Nit local variable fun variable(variable: Variable): RuntimeVariable @@@ -1360,18 -1359,6 +1366,18 @@@ return res end + # Generate an integer value + fun bool_instance(value: Bool): RuntimeVariable + do + var res = self.new_var(self.get_class("Bool").mclass_type) + if value then + self.add("{res} = 1;") + else + self.add("{res} = 0;") + end + return res + end + # Generate a string value fun string_instance(string: String): RuntimeVariable do @@@ -1392,19 -1379,6 +1398,19 @@@ return res end + fun value_instance(object: Object): RuntimeVariable + do + if object isa Int then + return int_instance(object) + else if object isa Bool then + return bool_instance(object) + else if object isa String then + return string_instance(object) + else + abort + end + end + # Generate an array value fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract @@@ -1845,7 -1819,6 +1851,7 @@@ redef class MMethodDe fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do var modelbuilder = v.compiler.modelbuilder + var val = constant_value if modelbuilder.mpropdef2npropdef.has_key(self) then var npropdef = modelbuilder.mpropdef2npropdef[self] var oldnode = v.current_node @@@ -1860,8 -1833,6 +1866,8 @@@ self.compile_parameter_check(v, arguments) nclassdef.compile_to_c(v, self, arguments) v.current_node = oldnode + else if val != null then + v.ret(v.value_instance(val)) else abort end @@@ -2465,11 -2436,7 +2471,7 @@@ redef class ASelfExp redef fun expr(v) do return v.frame.arguments.first end - redef class AContinueExpr - redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};") - end - - redef class ABreakExpr + redef class AEscapeExpr redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};") end @@@ -2532,10 -2499,7 +2534,7 @@@ redef class ADoExp redef fun stmt(v) do v.stmt(self.n_block) - var escapemark = self.escapemark - if escapemark != null then - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end + v.add_escape_label(break_mark) end end @@@ -2546,9 -2510,9 +2545,9 @@@ redef class AWhileExp var cond = v.expr_bool(self.n_expr) v.add("if (!{cond}) break;") v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) end end @@@ -2557,47 -2521,15 +2556,15 @@@ redef class ALoopExp do v.add("for(;;) \{") v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) end end redef class AForExpr redef fun stmt(v) do - # Shortcut on explicit range - # Avoid the instantiation of the range and the iterator - var nexpr = self.n_expr - if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then - var from = v.expr(nexpr.n_expr, null) - var to = v.expr(nexpr.n_expr2, null) - var variable = v.variable(variables.first) - var one = v.new_expr("1", v.get_class("Int").mclass_type) - - v.assign(variable, from) - v.add("for(;;) \{ /* shortcut range */") - - var ok - if nexpr isa AOrangeExpr then - ok = v.send(v.get_property("<", variable.mtype), [variable, to]) - else - ok = v.send(v.get_property("<=", variable.mtype), [variable, to]) - end - assert ok != null - v.add("if(!{ok}) break;") - - v.stmt(self.n_block) - - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") - var succ = v.send(v.get_property("successor", variable.mtype), [variable, one]) - assert succ != null - v.assign(variable, succ) - v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - return - end - var cl = v.expr(self.n_expr, null) var it_meth = self.method_iterator assert it_meth != null @@@ -2630,12 -2562,12 +2597,12 @@@ abort end v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) var next_meth = self.method_next assert next_meth != null v.compile_callsite(next_meth, [it]) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) var method_finish = self.method_finish if method_finish != null then @@@ -2782,7 -2714,7 +2749,7 @@@ redef class ACrangeExp var i2 = v.expr(self.n_expr2, null) var mtype = self.mtype.as(MClassType) var res = v.init_instance(mtype) - var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) + v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) return res end end @@@ -2794,7 -2726,7 +2761,7 @@@ redef class AOrangeExp var i2 = v.expr(self.n_expr2, null) var mtype = self.mtype.as(MClassType) var res = v.init_instance(mtype) - var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) + v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) return res end end @@@ -3060,6 -2992,9 +3027,6 @@@ en # Create a tool context to handle options and paths var toolcontext = new ToolContext -var opt_mixins = new OptionArray("Additionals module to min-in", "-m") -toolcontext.option_context.add_option(opt_mixins) - toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs." # We do not add other options, so process them now! @@@ -3078,6 -3013,7 +3045,6 @@@ en # Here we load an process all modules passed on the command line var mmodules = modelbuilder.parse(arguments) -var mixins = modelbuilder.parse(opt_mixins.value) if mmodules.is_empty then return modelbuilder.run_phases @@@ -3085,5 -3021,8 +3052,5 @@@ for mmodule in mmodules do toolcontext.info("*** PROCESS {mmodule} ***", 1) var ms = [mmodule] - if not mixins.is_empty then - ms.add_all mixins - end toolcontext.run_global_phases(ms) end diff --combined src/interpreter/naive_interpreter.nit index 8ffa78e,f557227..a04ec83 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@@ -20,11 -20,10 +20,11 @@@ module naive_interprete import literal import semantize private import parser::tables +import mixin redef class ToolContext # --discover-call-trace - var opt_discover_call_trace: OptionBool = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace") + var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace") redef init do @@@ -58,7 -57,7 +58,7 @@@ class NaiveInterprete # The modelbuilder that know the AST and its associations with the model var modelbuilder: ModelBuilder - # The main moduleof the program (used to lookup methoda + # The main module of the program (used to lookup method) var mainmodule: MModule # The command line arguments of the interpreted program @@@ -66,7 -65,6 +66,7 @@@ # arguments[1] is the first argument var arguments: Array[String] + # The main Sys instance var mainobj: nullable Instance init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String]) @@@ -114,43 -112,26 +114,26 @@@ # Set this mark to skip the evaluation until the end of the specified method frame var returnmark: nullable Frame = null - # Is a break executed? - # Set this mark to skip the evaluation until a labeled statement catch it with `is_break` - var breakmark: nullable EscapeMark = null - - # Is a continue executed? - # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue` - var continuemark: nullable EscapeMark = null + # Is a break or a continue executed? + # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape` + var escapemark: nullable EscapeMark = null # Is a return or a break or a continue executed? # Use this function to know if you must skip the evaluation of statements - fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null + fun is_escaping: Bool do return returnmark != null or escapemark != null # The value associated with the current return/break/continue, if any. # Set the value when you set a escapemark. # Read the value when you catch a mark or reach the end of a method var escapevalue: nullable Instance = null - # If there is a break and is associated with `escapemark`, then return true an clear the mark. - # If there is no break or if `escapemark` is null then return false. - # Use this function to catch a potential break. - fun is_break(escapemark: nullable EscapeMark): Bool - do - if escapemark != null and self.breakmark == escapemark then - self.breakmark = null - return true - else - return false - end - end - - # If there is a continue and is associated with `escapemark`, then return true an clear the mark. - # If there is no continue or if `escapemark` is null then return false. - # Use this function to catch a potential continue. - fun is_continue(escapemark: nullable EscapeMark): Bool + # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark. + # If there is no break/continue or if `escapemark` is null then return false. + # Use this function to catch a potential break/continue. + fun is_escape(escapemark: nullable EscapeMark): Bool do - if escapemark != null and self.continuemark == escapemark then - self.continuemark = null + if escapemark != null and self.escapemark == escapemark then + self.escapemark = null return true else return false @@@ -227,13 -208,13 +210,13 @@@ return new PrimitiveInstance[Float](ic.mclass_type, val) end - # The unique intance of the `true` value. + # The unique instance of the `true` value. var true_instance: Instance - # The unique intance of the `false` value. + # The unique instance of the `false` value. var false_instance: Instance - # The unique intance of the `null` value. + # The unique instance of the `null` value. var null_instance: Instance # Return a new array made of `values`. @@@ -249,19 -230,6 +232,19 @@@ return res end + fun value_instance(object: Object): Instance + do + if object isa Int then + return int_instance(object) + else if object isa Bool then + return bool_instance(object) + else if object isa String then + return string_instance(object) + else + abort + end + end + # Return a new native string initialized with `txt` fun native_string_instance(txt: String): Instance do @@@ -271,22 -239,13 +254,22 @@@ return new PrimitiveInstance[Buffer](ic.mclass_type, val) end + # Return a new String instance for `txt` + fun string_instance(txt: String): Instance + do + var nat = native_string_instance(txt) + var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)]) + assert res != null + return res + end + # The current frame used to store local variables of the current method executed fun frame: Frame do return frames.first # The stack of all frames. The first one is the current one. - var frames: List[Frame] = new List[Frame] + var frames = new List[Frame] - # Return a stack stace. One line per function + # Return a stack trace. One line per function fun stack_trace: String do var b = new FlatBuffer @@@ -333,7 -292,7 +316,7 @@@ f.map[v] = value end - # Store known method, used to trace methods as thez are reached + # Store known methods, used to trace methods as they are reached var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef] # Common code for calls to injected methods and normal methods @@@ -368,8 -327,8 +351,8 @@@ end # Execute `mpropdef` for a `args` (where `args[0]` is the receiver). - # Return a falue if `mpropdef` is a function, or null if it is a procedure. - # The call is direct/static. There is no message-seding/late-binding. + # Return a value if `mpropdef` is a function, or null if it is a procedure. + # The call is direct/static. There is no message-sending/late-binding. fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance do args = call_commons(mpropdef, args) @@@ -390,7 -349,6 +373,7 @@@ # Look for the AST node that implements the property var mproperty = mpropdef.mproperty + var val = mpropdef.constant_value if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef] self.parameter_check(npropdef, mpropdef, args) @@@ -399,8 -357,6 +382,8 @@@ var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef] self.parameter_check(nclassdef, mpropdef, args) return nclassdef.call(self, mpropdef, args) + else if val != null then + return value_instance(val) else fatal("Fatal Error: method {mpropdef} not found in the AST") abort @@@ -445,7 -401,7 +428,7 @@@ end # Execute a full `callsite` for given `args` - # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites + # Use this method, instead of `send` to execute and control the additional behavior of the call-sites fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance do var initializers = callsite.mpropdef.initializers @@@ -474,8 -430,8 +457,8 @@@ end # Execute `mproperty` for a `args` (where `args[0]` is the receiver). - # Return a falue if `mproperty` is a function, or null if it is a procedure. - # The call is polimotphic. There is a message-seding/late-bindng according to te receiver (args[0]). + # Return a value if `mproperty` is a function, or null if it is a procedure. + # The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]). fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance do var recv = args.first @@@ -522,7 -478,6 +505,7 @@@ var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do + if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue var n = self.modelbuilder.mclassdef2nclassdef[cd] for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then @@@ -535,7 -490,7 +518,7 @@@ return res end - var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]] + private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]] # Fill the initial values of the newly created instance `recv`. # `recv.mtype` is used to know what must be filled. @@@ -771,14 -726,14 +754,14 @@@ redef class AMethPropde var txt = recv.mtype.to_s return v.native_string_instance(txt) else if pname == "==" then - # == is correclt redefined for instances + # == is correctly redefined for instances return v.bool_instance(args[0] == args[1]) else if pname == "!=" then return v.bool_instance(args[0] != args[1]) else if pname == "is_same_type" then return v.bool_instance(args[0].mtype == args[1].mtype) else if pname == "is_same_instance" then - return v.bool_instance(args[1] != null and args[0].eq_is(args[1])) + return v.bool_instance(args[0].eq_is(args[1])) else if pname == "exit" then exit(args[1].to_i) abort @@@ -1227,20 -1182,7 +1210,7 @@@ redef class ASelfExp end end - redef class AContinueExpr - redef fun stmt(v) - do - var ne = self.n_expr - if ne != null then - var i = v.expr(ne) - if i == null then return - v.escapevalue = i - end - v.continuemark = self.escapemark - end - end - - redef class ABreakExpr + redef class AEscapeExpr redef fun stmt(v) do var ne = self.n_expr @@@ -1249,7 -1191,7 +1219,7 @@@ if i == null then return v.escapevalue = i end - v.breakmark = self.escapemark + v.escapemark = self.escapemark end end @@@ -1315,7 -1257,7 +1285,7 @@@ redef class ADoExp redef fun stmt(v) do v.stmt(self.n_block) - v.is_break(self.escapemark) # Clear the break (if any) + v.is_escape(self.break_mark) # Clear the break (if any) end end @@@ -1327,8 -1269,8 +1297,8 @@@ redef class AWhileExp if cond == null then return if not cond.is_true then return v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then return + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then return end end @@@ -1339,8 -1281,8 +1309,8 @@@ redef class ALoopExp do loop v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then return + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then return end end @@@ -1372,8 -1314,8 +1342,8 @@@ redef class AForExp abort end v.stmt(self.n_block) - if v.is_break(self.escapemark) then break - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then break + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then break v.callsite(method_next, [iter]) end @@@ -1492,7 -1434,9 +1462,7 @@@ redef class AStringFormExp redef fun expr(v) do var txt = self.value.as(not null) - var nat = v.native_string_instance(txt) - var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null) - return res + return v.string_instance(txt) end end @@@ -1592,6 -1536,7 +1562,6 @@@ redef class AAsNotnullExp do var i = v.expr(self.n_expr) if i == null then return null - var mtype = v.unanchor_type(self.mtype.as(not null)) if i.mtype isa MNullType then fatal(v, "Cast failed") end @@@ -1676,7 -1621,7 +1646,7 @@@ redef class ASuperExp var callsite = self.callsite if callsite != null then - # Add additionnals arguments for the super init call + # Add additional arguments for the super init call if args.length == 1 then for i in [0..callsite.msignature.arity[ do args.add(v.frame.arguments[i+1]) @@@ -1691,7 -1636,7 +1661,7 @@@ args = v.frame.arguments end - # stantard call-next-method + # standard call-next-method var mpropdef = self.mpropdef mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype) var res = v.call_without_varargs(mpropdef, args) diff --combined src/semantize/typing.nit index d5ff672,bd63c0f..5c9adeb --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@@ -47,7 -47,7 +47,7 @@@ private class TypeVisito # The analyzed property var mpropdef: nullable MPropDef - var selfvariable: Variable = new Variable("self") + var selfvariable = new Variable("self") # Is `self` use restricted? # * no explicit `self` @@@ -195,40 -195,6 +195,40 @@@ return sup end + # Special verification on != and == for null + # Return true + fun null_test(anode: ABinopExpr) + do + var mtype = anode.n_expr.mtype + var mtype2 = anode.n_expr2.mtype + + if mtype == null or mtype2 == null then return + + if not mtype2 isa MNullType then return + + # Check of useless null + if not mtype isa MNullableType then + if not anchor_to(mtype) isa MNullableType then + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") + end + return + end + + # Check for type adaptation + var variable = anode.n_expr.its_variable + if variable == null then return + + if anode isa AEqExpr then + anode.after_flow_context.when_true.set_var(variable, mtype2) + anode.after_flow_context.when_false.set_var(variable, mtype.mtype) + else if anode isa ANeExpr then + anode.after_flow_context.when_false.set_var(variable, mtype2) + anode.after_flow_context.when_true.set_var(variable, mtype.mtype) + else + abort + end + end + fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty do return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name) @@@ -429,6 -395,7 +429,6 @@@ fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType do if col.length == 1 then return col.first - var res = new Array[nullable MType] for t1 in col do if t1 == null then continue # return null var found = true @@@ -494,8 -461,8 +494,8 @@@ en redef class FlowContext # Store changes of types because of type evolution - private var vars: HashMap[Variable, nullable MType] = new HashMap[Variable, nullable MType] - private var cache: HashMap[Variable, nullable Array[nullable MType]] = new HashMap[Variable, nullable Array[nullable MType]] + private var vars = new HashMap[Variable, nullable MType] + private var cache = new HashMap[Variable, nullable Array[nullable MType]] # Adapt the variable to a static type # Warning1: do not modify vars directly. @@@ -780,7 -747,7 +780,7 @@@ redef class AContinueExp do var nexpr = self.n_expr if nexpr != null then - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) end self.is_typed = true end @@@ -791,7 -758,7 +791,7 @@@ redef class ABreakExp do var nexpr = self.n_expr if nexpr != null then - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) end self.is_typed = true end @@@ -804,9 -771,9 +804,9 @@@ redef class AReturnExp var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype if nexpr != null then if ret_type != null then - var mtype = v.visit_expr_subtype(nexpr, ret_type) + v.visit_expr_subtype(nexpr, ret_type) else - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) v.error(self, "Error: Return with value in a procedure.") end else if ret_type != null then @@@ -890,6 -857,9 +890,9 @@@ redef class AForExp var method_key: nullable CallSite var method_finish: nullable CallSite + var method_lt: nullable CallSite + var method_successor: nullable CallSite + private fun do_type_iterator(v: TypeVisitor, mtype: MType) do if mtype isa MNullType then @@@ -990,6 -960,19 +993,19 @@@ end self.method_key = keydef end + + if self.variables.length == 1 and n_expr isa ARangeExpr then + var variable = variables.first + var vtype = variable.declared_type.as(not null) + + if n_expr isa AOrangeExpr then + self.method_lt = v.get_method(self, vtype, "<", false) + else + self.method_lt = v.get_method(self, vtype, "<=", false) + end + + self.method_successor = v.get_method(self, vtype, "successor", false) + end end redef fun accept_typing(v) @@@ -1240,9 -1223,9 +1256,9 @@@ redef class AIsaExp var variable = self.n_expr.its_variable if variable != null then - var orig = self.n_expr.mtype - var from = if orig != null then orig.to_s else "invalid" - var to = if mtype != null then mtype.to_s else "invalid" + #var orig = self.n_expr.mtype + #var from = if orig != null then orig.to_s else "invalid" + #var to = if mtype != null then mtype.to_s else "invalid" #debug("adapt {variable}: {from} -> {to}") self.after_flow_context.when_true.set_var(variable, mtype) end @@@ -1369,7 -1352,16 +1385,7 @@@ redef class AEqExp redef fun accept_typing(v) do super - - var variable = self.n_expr.its_variable - if variable == null then return - var mtype = self.n_expr2.mtype - if not mtype isa MNullType then return - var vartype = v.get_variable(self, variable) - if not vartype isa MNullableType then return - self.after_flow_context.when_true.set_var(variable, mtype) - self.after_flow_context.when_false.set_var(variable, vartype.mtype) - #debug("adapt {variable}:{vartype} ; true->{mtype} false->{vartype.mtype}") + v.null_test(self) end end redef class ANeExpr @@@ -1377,7 -1369,16 +1393,7 @@@ redef fun accept_typing(v) do super - - var variable = self.n_expr.its_variable - if variable == null then return - var mtype = self.n_expr2.mtype - if not mtype isa MNullType then return - var vartype = v.get_variable(self, variable) - if not vartype isa MNullableType then return - self.after_flow_context.when_false.set_var(variable, mtype) - self.after_flow_context.when_true.set_var(variable, vartype.mtype) - #debug("adapt {variable}:{vartype} ; true->{vartype.mtype} false->{mtype}") + v.null_test(self) end end redef class ALtExpr