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 <r4pass@hotmail.com>
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
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
# 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
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
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
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
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)
do
gather_cc_paths
- var mainmodule = compiler.mainmodule
var compile_dir = compile_dir
# Generate the .h and .c files
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
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
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)
# 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)
# 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
# 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 <stdlib.h>")
self.header.add_decl("#include <stdio.h>")
self.header.add_decl("#include <string.h>")
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
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
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
# 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
# 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
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
# 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
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
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
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
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
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
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
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
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
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
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
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
# 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!
# 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
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
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
# 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
# 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])
# 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
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`.
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
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
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
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)
# 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)
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
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
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
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
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.
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
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
if i == null then return
v.escapevalue = i
end
- v.breakmark = self.escapemark
+ v.escapemark = self.escapemark
end
end
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
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
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
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
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
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
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])
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)
# 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`
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)
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
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.
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
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
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
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
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)
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
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
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