X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 0102c2b..e51269f 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -39,25 +39,25 @@ private class TypeVisitor # The static type of the receiver # Mainly used for type tests and type resolutions - var anchor: nullable MClassType + var anchor: nullable MClassType = null # The analyzed mclassdef - var mclassdef: nullable MClassDef + var mclassdef: nullable MClassDef = null # 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` # * 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(modelbuilder: ModelBuilder, mmodule: MModule, mpropdef: nullable MPropDef) + init do - self.modelbuilder = modelbuilder - self.mmodule = mmodule + var mpropdef = self.mpropdef if mpropdef != null then self.mpropdef = mpropdef @@ -72,7 +72,7 @@ private class TypeVisitor selfvariable.declared_type = mclass.mclass_type var mprop = mpropdef.mproperty - if mprop isa MMethod and mprop.is_toplevel then + if mprop isa MMethod and mprop.is_new then is_toplevel_context = true end end @@ -101,9 +101,9 @@ private class TypeVisitor end # Check that `sub` is a subtype of `sup`. - # If `sub` is not a valud suptype, then display an error on `node` an return null. + # If `sub` is not a valid suptype, then display an error on `node` an return null. # If `sub` is a safe subtype of `sup` then return `sub`. - # If `sub` is an insafe subtype (ie an imlicit cast is required), then return `sup`. + # If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`. # # The point of the return type is to determinate the usable type on an expression: # If the suptype is safe, then the return type is the one on the expression typed by `sub`. @@ -112,11 +112,16 @@ private class TypeVisitor do if self.is_subtype(sub, sup) then return sub if self.is_subtype(sub, self.anchor_to(sup)) then - # FIXME workarround to the current unsafe typing policy. To remove once fixed virtual types exists. + # FIXME workaround to the current unsafe typing policy. To remove once fixed virtual types exists. #node.debug("Unsafe typing: expected {sup}, got {sub}") return sup end - self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + if sub.need_anchor then + var u = anchor_to(sub) + self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}") + else + self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") + end return null end @@ -179,7 +184,7 @@ private class TypeVisitor end - private fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType + fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType do var sub = visit_expr(nexpr) if sub == null then return null # Forward error @@ -195,6 +200,68 @@ private class TypeVisitor return sup end + # Can `mtype` be null (up to the current knowledge)? + fun can_be_null(mtype: MType): Bool + do + if mtype isa MNullableType or mtype isa MNullType then return true + if mtype isa MFormalType then + var x = anchor_to(mtype) + if x isa MNullableType or x isa MNullType then return true + end + return false + end + + # Check that `mtype` can be null (up to the current knowledge). + # + # If not then display a `useless-null-test` warning on node and return false. + # Else return true. + fun check_can_be_null(anode: ANode, mtype: MType): Bool + do + if can_be_null(mtype) then return true + + if mtype isa MFormalType then + var res = anchor_to(mtype) + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}: {res}`.") + else + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") + end + return false + 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 + + 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 + + # Check for type adaptation + var variable = anode.n_expr.its_variable + if variable == null then return + + # 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) + 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) + 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) @@ -213,10 +280,7 @@ private class TypeVisitor fun get_mclass(node: ANode, name: String): nullable MClass do - var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name) - if mclass == null then - self.modelbuilder.error(node, "Type Error: missing primitive class `{name}'.") - end + var mclass = modelbuilder.get_mclass_by_name(node, mmodule, name) return mclass end @@ -233,11 +297,23 @@ private class TypeVisitor #debug("recv: {recvtype} (aka {unsafe_type})") if recvtype isa MNullType then - self.error(node, "Error: Method '{name}' call on 'null'.") - return null + # `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}' call on 'null'.") + return null + end end var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + if name == "new" and mproperty == null then + name = "init" + mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + end + if mproperty == null then #self.modelbuilder.error(node, "Type error: property {name} not found in {unsafe_type} (ie {recvtype})") if recv_is_self then @@ -285,13 +361,14 @@ private class TypeVisitor end - var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var msignature = mpropdef.new_msignature or else mpropdef.msignature + if msignature == null then return null # skip error msignature = resolve_for(msignature, recvtype, recv_is_self).as(MSignature) var erasure_cast = false var rettype = mpropdef.msignature.return_mtype if not recv_is_self and rettype != null then - rettype = rettype.as_notnullable + rettype = rettype.undecorate if rettype isa MParameterType then var erased_rettype = msignature.return_mtype assert erased_rettype != null @@ -304,7 +381,16 @@ private class TypeVisitor return callsite end - # Visit the expressions of args and cheik their conformity with the corresponding typi in signature + fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite + do + var unsafe_type = self.anchor_to(recvtype) + var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + if mproperty == null then return null + return get_method(node, recvtype, name, recv_is_self) + end + + + # Visit the expressions of args and check their conformity with the corresponding type in signature # The point of this method is to handle varargs correctly # Note: The signature must be correctly adapted fun check_signature(node: ANode, args: Array[AExpr], name: String, msignature: MSignature): Bool @@ -334,11 +420,18 @@ private class TypeVisitor self.visit_expr_subtype(args[j], paramtype) end if vararg_rank >= 0 then - var varargs = new Array[AExpr] var paramtype = msignature.mparameters[vararg_rank].mtype - for j in [vararg_rank..vararg_rank+vararg_decl] do - varargs.add(args[j]) - self.visit_expr_subtype(args[j], paramtype) + var first = args[vararg_rank] + if vararg_decl == 0 and first isa AVarargExpr then + var mclass = get_mclass(node, "Array") + if mclass == null then return false # Forward error + var array_mtype = mclass.get_mtype([paramtype]) + self.visit_expr_subtype(first.n_expr, array_mtype) + first.mtype = first.n_expr.mtype + else + for j in [vararg_rank..vararg_rank+vararg_decl] do + self.visit_expr_subtype(args[j], paramtype) + end end end return true @@ -386,13 +479,12 @@ private class TypeVisitor 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 for t2 in col do if t2 == null then continue # return null - if t2 isa MNullableType or t2 isa MNullType then + if can_be_null(t2) and not can_be_null(t1) then t1 = t1.as_nullable end if not is_subtype(t2, t1) then found = false @@ -409,7 +501,7 @@ end # A specific method call site with its associated informations. class CallSite - # The assiciated node for location + # The associated node for location var node: ANode # The static type of the receiver (possibly unresolved) @@ -422,7 +514,7 @@ class CallSite var anchor: nullable MClassType # Is the receiver self? - # If "for_self", virtual types of the signature are keeped + # If "for_self", virtual types of the signature are kept # If "not_for_self", virtual type are erased var recv_is_self: Bool @@ -452,12 +544,12 @@ end 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. - # Warning2: sub-flow may have cached a unadapted variabial + # Warning2: sub-flow may have cached a unadapted variable private fun set_var(variable: Variable, mtype: nullable MType) do self.vars[variable] = mtype @@ -501,7 +593,7 @@ redef class APropdef do end - # The variable associated to the reciever (if any) + # The variable associated to the receiver (if any) var selfvariable: nullable Variable end @@ -511,14 +603,18 @@ redef class AMethPropdef var nblock = self.n_block if nblock == null then return - var mpropdef = self.mpropdef.as(not null) + var mpropdef = self.mpropdef + if mpropdef == null then return # skip error + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable var mmethoddef = self.mpropdef.as(not null) - for i in [0..mmethoddef.msignature.arity[ do - var mtype = mmethoddef.msignature.mparameters[i].mtype - if mmethoddef.msignature.vararg_rank == i then + var msignature = mmethoddef.msignature + if msignature == null then return # skip error + for i in [0..msignature.arity[ do + var mtype = msignature.mparameters[i].mtype + if msignature.vararg_rank == i then var arrayclass = v.get_mclass(self.n_signature.n_params[i], "Array") if arrayclass == null then return # Skip error mtype = arrayclass.get_mtype([mtype]) @@ -529,7 +625,7 @@ redef class AMethPropdef end v.visit_stmt(nblock) - if not nblock.after_flow_context.is_unreachable and mmethoddef.msignature.return_mtype != null then + 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 v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).") end @@ -539,7 +635,11 @@ end redef class AAttrPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var mpropdef = self.mpropdef.as(not null) + if not has_value then return + + var mpropdef = self.mpropdef + if mpropdef == null then return # skip error + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable @@ -548,6 +648,14 @@ redef class AAttrPropdef var mtype = self.mpropdef.static_mtype v.visit_expr_subtype(nexpr, mtype) end + var nblock = self.n_block + if nblock != null then + v.visit_stmt(nblock) + if not nblock.after_flow_context.is_unreachable then + # We reach the end of the init without having a return, it is bad + v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).") + end + end end end @@ -576,6 +684,12 @@ redef class AExpr do v.error(self, "no implemented accept_typing for {self.class_name}") end + + # Is non-null if `self` is a leaf of a comprehension array construction. + # In this case, the enclosing literal array node is designated. + # The result of the evaluation of `self` must be + # stored inside the designated array (there is an implicit `push`) + var comprehension: nullable AArrayExpr = null end redef class ABlockExpr @@ -611,7 +725,11 @@ redef class AVardeclExpr var nexpr = self.n_expr if nexpr != null then if mtype != null then - v.visit_expr_subtype(nexpr, mtype) + var etype = v.visit_expr_subtype(nexpr, mtype) + if etype == mtype then + assert ntype != null + v.modelbuilder.advice(ntype, "useless-type", "Warning: useless type definition for variable `{variable.name}`") + end else mtype = v.visit_expr(nexpr) if mtype == null then return # Skip error @@ -620,7 +738,9 @@ redef class AVardeclExpr var decltype = mtype if mtype == null or mtype isa MNullType then - decltype = v.get_mclass(self, "Object").mclass_type.as_nullable + var objclass = v.get_mclass(self, "Object") + if objclass == null then return # skip error + decltype = objclass.mclass_type.as_nullable if mtype == null then mtype = decltype end @@ -629,6 +749,7 @@ redef class AVardeclExpr #debug("var {variable}: {mtype}") + self.mtype = mtype self.is_typed = true end end @@ -689,11 +810,6 @@ redef class AReassignFormExpr self.read_type = readtype - if readtype isa MNullType then - v.error(self, "Error: Method '{reassign_name}' call on 'null'.") - return null - end - var callsite = v.get_method(self, readtype, reassign_name, false) if callsite == null then return null # Skip error self.reassign_callsite = callsite @@ -738,7 +854,7 @@ redef class AContinueExpr 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 @@ -749,7 +865,7 @@ redef class ABreakExpr 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 @@ -759,12 +875,20 @@ redef class AReturnExpr redef fun accept_typing(v) do var nexpr = self.n_expr - var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype + var ret_type + var mpropdef = v.mpropdef + if mpropdef isa MMethodDef then + ret_type = mpropdef.msignature.return_mtype + else if mpropdef isa MAttributeDef then + ret_type = mpropdef.static_mtype + else + abort + end 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 @@ -788,7 +912,12 @@ redef class AIfExpr v.visit_stmt(n_then) v.visit_stmt(n_else) + self.is_typed = true + + if n_then != null and n_else == null then + self.mtype = n_then.mtype + end end end @@ -846,6 +975,10 @@ redef class AForExpr var method_item: nullable CallSite var method_next: nullable CallSite 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 @@ -891,7 +1024,7 @@ redef class AForExpr is_col = true end - if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type, objcla.mclass_type.as_nullable])) then + if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then # Map Iterator var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla) var variables = self.variables @@ -912,7 +1045,7 @@ redef class AForExpr # anchor formal and virtual types if mtype.need_anchor then mtype = v.anchor_to(mtype) - mtype = mtype.as_notnullable + mtype = mtype.undecorate self.coltype = mtype.as(MClassType) # get methods is_ok, next, item @@ -937,6 +1070,8 @@ redef class AForExpr end self.method_next = nextdef + self.method_finish = v.try_get_method(self, ittype, "finish", false) + if is_map then var keydef = v.get_method(self, ittype, "key", false) if keydef == null then @@ -945,6 +1080,19 @@ redef class AForExpr 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) @@ -955,6 +1103,25 @@ redef class AForExpr 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 end @@ -1015,12 +1182,18 @@ redef class AOrElseExpr return # Skip error end - t1 = t1.as_notnullable + if t1 isa MNullType then + v.error(n_expr, "Type error: or else on null") + else if v.check_can_be_null(n_expr, t1) then + t1 = t1.as_notnull + end var t = v.merge_types(self, [t1, t2]) if t == null then - t = v.mmodule.object_type - if t2 isa MNullableType then + var c = v.get_mclass(self, "Object") + if c == null then return # forward error + t = c.mclass_type + if v.can_be_null(t2) then t = t.as_nullable end #v.error(self, "Type Error: ambiguous type {t1} vs {t2}") @@ -1085,31 +1258,78 @@ redef class ASuperstringExpr var mclass = v.get_mclass(self, "String") if mclass == null then return # Forward error self.mtype = mclass.mclass_type + var objclass = v.get_mclass(self, "Object") + if objclass == null then return # Forward error + var objtype = objclass.mclass_type for nexpr in self.n_exprs do - v.visit_expr_subtype(nexpr, v.mmodule.object_type) + v.visit_expr_subtype(nexpr, objtype) end end end redef class AArrayExpr + # The `with_capacity` method on Array var with_capacity_callsite: nullable CallSite + + # The `push` method on arrays var push_callsite: nullable CallSite + # The element of each type + var element_mtype: nullable MType + + # Set that `self` is a part of comprehension array `na` + # If `self` is a `for`, or a `if`, then `set_comprehension` is recursively applied. + private fun set_comprehension(n: nullable AExpr) + do + if n == null then + return + else if n isa AForExpr then + set_comprehension(n.n_block) + else if n isa AIfExpr then + set_comprehension(n.n_then) + set_comprehension(n.n_else) + else + # is a leave + n.comprehension = self + end + end redef fun accept_typing(v) do + var mtype: nullable MType = null + var ntype = self.n_type + if ntype != null then + mtype = v.resolve_mtype(ntype) + if mtype == null then return # Skip error + end var mtypes = new Array[nullable MType] - for e in self.n_exprs.n_exprs do + var useless = false + for e in self.n_exprs do var t = v.visit_expr(e) if t == null then return # Skip error end - mtypes.add(t) + set_comprehension(e) + if mtype != null then + if v.check_subtype(e, t, mtype) == null then return # Skip error + if t == mtype then useless = true + else + mtypes.add(t) + end end - var mtype = v.merge_types(self, mtypes) if mtype == null then + mtype = v.merge_types(self, mtypes) + end + if mtype == null or mtype isa MNullType then v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}") return end + if useless then + assert ntype != null + v.modelbuilder.warning(ntype, "useless-type", "Warning: useless type declaration `{mtype}` in literal Array since it can be inferred from the elements type.") + end + + self.element_mtype = mtype + var mclass = v.get_mclass(self, "Array") if mclass == null then return # Forward error var array_mtype = mclass.get_mtype([mtype]) @@ -1177,9 +1397,9 @@ redef class AIsaExpr 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 @@ -1205,26 +1425,23 @@ redef class AAsNotnullExpr v.error(self, "Type error: as(not null) on null") return end - if mtype isa MNullableType then - self.mtype = mtype.mtype - return + + if v.check_can_be_null(n_expr, mtype) then + mtype = mtype.as_notnull end + self.mtype = mtype + end +end - if mtype isa MClassType then - v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}`.") - return - end - assert mtype.need_anchor - var u = v.anchor_to(mtype) - if not u isa MNullableType then - v.modelbuilder.warning(self, "useless-type-test", "Warning: expression is already not null, since it is a `{mtype}: {u}`.") - return - end +redef class AParExpr + redef fun accept_typing(v) + do + self.mtype = v.visit_expr(self.n_expr) end end -redef class AProxyExpr +redef class AOnceExpr redef fun accept_typing(v) do self.mtype = v.visit_expr(self.n_expr) @@ -1244,6 +1461,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 @@ -1252,17 +1478,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 - if recvtype isa MNullType then - v.error(self, "Error: Method '{name}' call on 'null'.") - 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 - var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr) - if callsite == null then return self.callsite = callsite var msignature = callsite.msignature @@ -1306,16 +1553,7 @@ redef class AEqExpr 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 @@ -1323,16 +1561,7 @@ 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 @@ -1365,6 +1594,9 @@ end redef class AStarExpr redef fun property_name do return "*" end +redef class AStarstarExpr + redef fun property_name do return "**" +end redef class ASlashExpr redef fun property_name do return "/" end @@ -1418,10 +1650,6 @@ redef class ASendReassignFormExpr var name = self.property_name if recvtype == null then return # Forward error - if recvtype isa MNullType then - v.error(self, "Error: Method '{name}' call on 'null'.") - return - end var for_self = self.n_expr isa ASelfExpr var callsite = v.get_method(self, recvtype, name, for_self) @@ -1486,14 +1714,16 @@ redef class ASuperExpr redef fun accept_typing(v) do - var recvtype = v.anchor + var anchor = v.anchor + assert anchor != null + var recvtype = v.get_variable(self, v.selfvariable) assert recvtype != null var mproperty = v.mpropdef.mproperty if not mproperty isa MMethod then v.error(self, "Error: super only usable in a method") return end - var superprops = mproperty.lookup_super_definitions(v.mmodule, recvtype) + var superprops = mproperty.lookup_super_definitions(v.mmodule, anchor) if superprops.length == 0 then if mproperty.is_init and v.mpropdef.is_intro then process_superinit(v) @@ -1519,18 +1749,20 @@ redef class ASuperExpr private fun process_superinit(v: TypeVisitor) do - var recvtype = v.anchor + var anchor = v.anchor + assert anchor != null + var recvtype = v.get_variable(self, v.selfvariable) assert recvtype != null var mpropdef = v.mpropdef assert mpropdef isa MMethodDef var mproperty = mpropdef.mproperty var superprop: nullable MMethodDef = null for msupertype in mpropdef.mclassdef.supertypes do - msupertype = msupertype.anchor_to(v.mmodule, recvtype) + msupertype = msupertype.anchor_to(v.mmodule, anchor) var errcount = v.modelbuilder.toolcontext.error_count var candidate = v.try_get_mproperty_by_name2(self, msupertype, mproperty.name).as(nullable MMethod) if candidate == null then - if v.modelbuilder.toolcontext.error_count > errcount then return # Forard error + if v.modelbuilder.toolcontext.error_count > errcount then return # Forward error continue # Try next super-class end if superprop != null and candidate.is_root_init then @@ -1540,13 +1772,13 @@ redef class ASuperExpr v.error(self, "Error: conflicting super constructor to call for {mproperty}: {candidate.full_name}, {superprop.mproperty.full_name}") return end - var candidatedefs = candidate.lookup_definitions(v.mmodule, recvtype) + var candidatedefs = candidate.lookup_definitions(v.mmodule, anchor) if superprop != null and superprop.mproperty == candidate then if superprop == candidatedefs.first then continue candidatedefs.add(superprop) end if candidatedefs.length > 1 then - v.error(self, "Error: confliting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}") + v.error(self, "Error: conflicting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}") return end superprop = candidatedefs.first @@ -1593,11 +1825,13 @@ redef class ANewExpr # The constructor invoked by the new. var callsite: nullable CallSite + # The designated type + var recvtype: nullable MClassType + redef fun accept_typing(v) do var recvtype = v.resolve_mtype(self.n_type) if recvtype == null then return - self.mtype = recvtype if not recvtype isa MClassType then if recvtype isa MNullableType then @@ -1607,26 +1841,32 @@ redef class ANewExpr v.error(self, "Type error: cannot instantiate the formal type {recvtype}.") return end - else - if recvtype.mclass.kind == abstract_kind then - v.error(self, "Cannot instantiate abstract class {recvtype}.") - return - else if recvtype.mclass.kind == interface_kind then - v.error(self, "Cannot instantiate interface {recvtype}.") - return - end end + self.recvtype = recvtype + var name: String var nid = self.n_id if nid != null then name = nid.text else - name = "init" + name = "new" 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 + end + self.mtype = recvtype + else + self.mtype = callsite.msignature.return_mtype + assert self.mtype != null + end + self.callsite = callsite if not callsite.mproperty.is_init_for(recvtype.mclass) then @@ -1671,7 +1911,8 @@ redef class AAttrFormExpr var mpropdefs = mproperty.lookup_definitions(v.mmodule, unsafe_type) assert mpropdefs.length == 1 var mpropdef = mpropdefs.first - var attr_type = mpropdef.static_mtype.as(not null) + var attr_type = mpropdef.static_mtype + if attr_type == null then return # skip error attr_type = v.resolve_for(attr_type, recvtype, self.n_expr isa ASelfExpr) self.attr_type = attr_type end @@ -1726,6 +1967,16 @@ redef class AIssetAttrExpr end end +redef class AVarargExpr + redef fun accept_typing(v) + do + # This kind of pseudo-expression can be only processed trough a signature + # See `check_signature` + # Other cases are a syntax error. + v.error(self, "Syntax error: unexpected `...`") + end +end + ### redef class ADebugTypeExpr