X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 38a689a..a46f632 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -52,6 +52,7 @@ private class TypeVisitor # Is `self` use restricted? # * no explicit `self` # * method called on the implicit self must be top-level + # Currently only used for `new` factory since there is no valid receiver inside var is_toplevel_context = false init @@ -71,7 +72,7 @@ private class TypeVisitor selfvariable.declared_type = mclass.mclass_type var mprop = mpropdef.mproperty - if mprop isa MMethod and (mprop.is_toplevel or mprop.is_new) then + if mprop isa MMethod and mprop.is_new then is_toplevel_context = true end end @@ -238,12 +239,16 @@ private class TypeVisitor if not mtype2 isa MNullType then return - if mtype isa MNullType then return - # Check of useless null if not check_can_be_null(anode.n_expr, mtype) then return - mtype = mtype.as_notnull + if mtype isa MNullType then + # Because of type adaptation, we cannot just stop here + # so return use `null` as a bottom type that will be merged easily (cf) `merge_types` + mtype = null + else + mtype = mtype.as_notnull + end # Check for type adaptation var variable = anode.n_expr.its_variable @@ -251,11 +256,11 @@ private class TypeVisitor # One is null (mtype2 see above) the other is not null 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) + anode.after_flow_context.when_true.set_var(self, variable, mtype2) + anode.after_flow_context.when_false.set_var(self, variable, 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) + anode.after_flow_context.when_false.set_var(self, variable, mtype2) + anode.after_flow_context.when_true.set_var(self, variable, mtype) else abort end @@ -443,6 +448,8 @@ private class TypeVisitor fun get_variable(node: AExpr, variable: Variable): nullable MType do + if not variable.is_adapted then return variable.declared_type + var flow = node.after_flow_context if flow == null then self.error(node, "No context!") @@ -455,7 +462,7 @@ private class TypeVisitor #node.debug("*** START Collected for {variable}") var mtypes = flow.collect_types(variable) #node.debug("**** END Collected for {variable}") - if mtypes == null or mtypes.length == 0 then + if mtypes.length == 0 then return variable.declared_type else if mtypes.length == 1 then return mtypes.first @@ -467,12 +474,18 @@ private class TypeVisitor end end + # Some variables where type-adapted during the visit + var dirty = false + + # Some loops had been visited during the visit + var has_loop = false + fun set_variable(node: AExpr, variable: Variable, mtype: nullable MType) do var flow = node.after_flow_context assert flow != null - flow.set_var(variable, mtype) + flow.set_var(self, variable, mtype) end fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType @@ -539,49 +552,58 @@ end redef class Variable # The declared type of the variable var declared_type: nullable MType + + # Was the variable type-adapted? + # This is used to speedup type retrieval while it remains `false` + private var is_adapted = false end redef class FlowContext # Store changes of types because of type evolution 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. # Warning2: sub-flow may have cached a unadapted variable - private fun set_var(variable: Variable, mtype: nullable MType) + private fun set_var(v: TypeVisitor, variable: Variable, mtype: nullable MType) do + if variable.declared_type == mtype and not variable.is_adapted then return + if vars.has_key(variable) and vars[variable] == mtype then return self.vars[variable] = mtype - self.cache.keys.remove(variable) + v.dirty = true + variable.is_adapted = true + #node.debug "set {variable} to {mtype or else "X"}" end - private fun collect_types(variable: Variable): nullable Array[nullable MType] + # Look in the flow and previous flow and collect all first reachable type adaptation of a local variable + private fun collect_types(variable: Variable): Array[nullable MType] do - if cache.has_key(variable) then - return cache[variable] - end - var res: nullable Array[nullable MType] = null - if vars.has_key(variable) then - var mtype = vars[variable] - res = [mtype] - else if self.previous.is_empty then - # Root flow - res = [variable.declared_type] - else - for flow in self.previous do - if flow.is_unreachable then continue - var r2 = flow.collect_types(variable) - if r2 == null then continue - if res == null then - res = r2.to_a - else - for t in r2 do - if not res.has(t) then res.add(t) - end + #node.debug "flow for {variable}" + var res = new Array[nullable MType] + + var todo = [self] + var seen = new HashSet[FlowContext] + while not todo.is_empty do + var f = todo.pop + if f.is_unreachable then continue + if seen.has(f) then continue + seen.add f + + if f.vars.has_key(variable) then + # Found something. Collect it and do not process further on this path + res.add f.vars[variable] + #f.node.debug "process {variable}: got {f.vars[variable] or else "X"}" + else + todo.add_all f.previous + todo.add_all f.loops + if f.previous.is_empty then + # Root flowcontext mean a parameter or something related + res.add variable.declared_type + #f.node.debug "root process {variable}: got {variable.declared_type or else "X"}" end end end - cache[variable] = res + #self.node.debug "##### end flow for {variable}: {res.join(" ")}" return res end end @@ -622,7 +644,12 @@ redef class AMethPropdef assert variable != null variable.declared_type = mtype end - v.visit_stmt(nblock) + + loop + v.dirty = false + v.visit_stmt(nblock) + if not v.has_loop or not v.dirty then break + end if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then # We reach the end of the function without having a return, it is bad @@ -748,6 +775,7 @@ redef class AVardeclExpr #debug("var {variable}: {mtype}") + self.mtype = mtype self.is_typed = true end end @@ -950,8 +978,8 @@ end redef class AWhileExpr redef fun accept_typing(v) do + v.has_loop = true v.visit_expr_bool(n_expr) - v.visit_stmt(n_block) self.is_typed = true end @@ -960,6 +988,7 @@ end redef class ALoopExpr redef fun accept_typing(v) do + v.has_loop = true v.visit_stmt(n_block) self.is_typed = true end @@ -1095,12 +1124,32 @@ redef class AForExpr redef fun accept_typing(v) do + v.has_loop = true var mtype = v.visit_expr(n_expr) if mtype == null then return self.do_type_iterator(v, mtype) v.visit_stmt(n_block) + + self.mtype = n_block.mtype + self.is_typed = true + end +end + +redef class AWithExpr + var method_start: nullable CallSite + var method_finish: nullable CallSite + + redef fun accept_typing(v: TypeVisitor) + do + var mtype = v.visit_expr(n_expr) + if mtype == null then return + + method_start = v.get_method(self, mtype, "start", n_expr isa ASelfExpr) + method_finish = v.get_method(self, mtype, "finish", n_expr isa ASelfExpr) + + v.visit_stmt(n_block) self.mtype = n_block.mtype self.is_typed = true end @@ -1297,6 +1346,8 @@ redef class AArrayExpr end end if mtype == null then + # Ensure monotony for type adaptation on loops + if self.element_mtype != null then mtypes.add self.element_mtype mtype = v.merge_types(self, mtypes) end if mtype == null or mtype isa MNullType then @@ -1381,7 +1432,7 @@ redef class AIsaExpr #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) + self.after_flow_context.when_true.set_var(v, variable, mtype) end self.mtype = v.type_bool(self) @@ -1441,6 +1492,15 @@ redef class ASelfExpr end end +redef class AImplicitSelfExpr + # Is the implicit receiver `sys`? + # + # By default, the implicit receiver is `self`. + # But when there is not method for `self`, `sys` is used as a fall-back. + # Is this case this flag is set to `true`. + var is_sys = false +end + ## MESSAGE SENDING AND PROPERTY redef class ASendExpr @@ -1449,13 +1509,38 @@ redef class ASendExpr redef fun accept_typing(v) do - var recvtype = v.visit_expr(self.n_expr) + var nrecv = self.n_expr + var recvtype = v.visit_expr(nrecv) var name = self.property_name if recvtype == null then return # Forward error - var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr) - if callsite == null then return + var callsite = null + var unsafe_type = v.anchor_to(recvtype) + var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name) + if mproperty == null and nrecv isa AImplicitSelfExpr then + # Special fall-back search in `sys` when noting found in the implicit receiver. + var sysclass = v.try_get_mclass(self, "Sys") + if sysclass != null then + var systype = sysclass.mclass_type + mproperty = v.try_get_mproperty_by_name2(self, systype, name) + if mproperty != null then + callsite = v.get_method(self, systype, name, false) + if callsite == null then return # Forward error + # Update information, we are looking at `sys` now, not `self` + nrecv.is_sys = true + nrecv.its_variable = null + nrecv.mtype = systype + recvtype = systype + end + end + end + if callsite == null then + # If still nothing, just exit + callsite = v.get_method(self, recvtype, name, nrecv isa ASelfExpr) + if callsite == null then return + end + self.callsite = callsite var msignature = callsite.msignature @@ -1790,6 +1875,7 @@ redef class ANewExpr end self.recvtype = recvtype + var kind = recvtype.mclass.kind var name: String var nid = self.n_id @@ -1798,11 +1884,24 @@ redef class ANewExpr else name = "new" end + if name == "intern" then + if kind != concrete_kind then + v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.") + return + end + if n_args.n_exprs.not_empty then + v.error(n_args, "Type Error: the intern constructor expects no arguments.") + return + end + # Our job is done + self.mtype = recvtype + return + end + var callsite = v.get_method(self, recvtype, name, false) if callsite == null then return if not callsite.mproperty.is_new then - var kind = recvtype.mclass.kind if kind != concrete_kind then v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.") return