X-Git-Url: http://nitlanguage.org diff --git a/src/typing.nit b/src/typing.nit index c1c217e..d2cc5fa 100644 --- a/src/typing.nit +++ b/src/typing.nit @@ -19,70 +19,107 @@ module typing import flow -import modelbuilder +import modelize_property +import phase +import local_var_init + +redef class ToolContext + var typing_phase: Phase = new TypingPhase(self, [flow_phase, modelize_property_phase, local_var_init_phase]) +end + +private class TypingPhase + super Phase + redef fun process_npropdef(npropdef) do npropdef.do_typing(toolcontext.modelbuilder) +end private class TypeVisitor var modelbuilder: ModelBuilder - var nclassdef: AClassdef - var mpropdef: MPropDef + + # The module of the analysis + # Used to correctly query the model + var mmodule: MModule + + # The static type of the receiver + # Mainly used for type tests and type resolutions + var anchor: nullable MClassType + + # The analyzed mclassdef + var mclassdef: nullable MClassDef + + # The analyzed property + var mpropdef: nullable MPropDef var selfvariable: Variable = new Variable("self") - init(modelbuilder: ModelBuilder, nclassdef: AClassdef, mpropdef: MPropDef) + # Is `self` use restricted? + # * no explicit `self` + # * method called on the implicit self must be top-level + var is_toplevel_context = false + + init(modelbuilder: ModelBuilder, mmodule: MModule, mpropdef: nullable MPropDef) do self.modelbuilder = modelbuilder - self.nclassdef = nclassdef - self.mpropdef = mpropdef + self.mmodule = mmodule - var mclass = nclassdef.mclassdef.mclass + if mpropdef != null then + self.mpropdef = mpropdef + var mclassdef = mpropdef.mclassdef + self.mclassdef = mclassdef + self.anchor = mclassdef.bound_mtype - var selfvariable = new Variable("self") - self.selfvariable = selfvariable - selfvariable.declared_type = mclass.mclass_type - end + var mclass = mclassdef.mclass - fun mmodule: MModule do return self.nclassdef.mclassdef.mmodule + var selfvariable = new Variable("self") + self.selfvariable = selfvariable + selfvariable.declared_type = mclass.mclass_type - fun anchor: MClassType do return self.nclassdef.mclassdef.bound_mtype + var mprop = mpropdef.mproperty + if mprop isa MMethod and mprop.is_toplevel then + is_toplevel_context = true + end + end + end fun anchor_to(mtype: MType): MType do - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype + var anchor = anchor + if anchor == null then + assert not mtype.need_anchor + return mtype + end return mtype.anchor_to(mmodule, anchor) end fun is_subtype(sub, sup: MType): Bool do - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype return sub.is_subtype(mmodule, anchor, sup) end fun resolve_for(mtype, subtype: MType, for_self: Bool): MType do - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype #print "resolve_for {mtype} sub={subtype} forself={for_self} mmodule={mmodule} anchor={anchor}" var res = mtype.resolve_for(subtype, anchor, mmodule, not for_self) return res end - fun resolve_signature_for(msignature: MSignature, recv: MType, for_self: Bool): MSignature - do - return self.resolve_for(msignature, recv, for_self).as(MSignature) - end - - fun check_subtype(node: ANode, sub, sup: MType): Bool + # 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 a safe subtype of `sup` then return `sub`. + # If `sub` is an insafe subtype (ie an imlicit 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`. + # Is the subtype is unsafe, then the return type is the one of an implicit cast on `sup`. + fun check_subtype(node: ANode, sub, sup: MType): nullable MType do - if self.is_subtype(sub, sup) then return true + 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. #node.debug("Unsafe typing: expected {sup}, got {sub}") - return true + return sup end self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}") - return false + return null end # Visit an expression and do not care about the return value @@ -96,7 +133,7 @@ private class TypeVisitor # Return the type of the expression # Display an error and return null if: # * the type cannot be determined or - # * `nexpr' is a statement + # * `nexpr` is a statement fun visit_expr(nexpr: AExpr): nullable MType do nexpr.accept_typing(self) @@ -114,11 +151,11 @@ private class TypeVisitor return null end - # Visit an expression and expect its static type is a least a `sup' - # Return the type of the expression + # Visit an expression and expect its static type is a least a `sup` + # Return the type of the expression or null if # * the type cannot be determined or - # * `nexpr' is a statement - # * `nexpt' is not a `sup' + # * `nexpr` is a statement or + # * `nexpr` is not a `sup` fun visit_expr_subtype(nexpr: AExpr, sup: nullable MType): nullable MType do var sub = visit_expr(nexpr) @@ -126,17 +163,18 @@ private class TypeVisitor if sup == null then return null # Forward error - if not check_subtype(nexpr, sub, sup) then - return null + var res = check_subtype(nexpr, sub, sup) + if res != sub then + nexpr.implicit_cast_to = res end - return sub + return res end - # Visit an expression and expect its static type is a bool - # Return the type of the expression + # Visit an expression and expect its static type is a `Bool` + # Return the type of the expression or null if # * the type cannot be determined or - # * `nexpr' is a statement - # * `nexpt' is not a `sup' + # * `nexpr` is a statement or + # * `nexpr` is not a `Bool` fun visit_expr_bool(nexpr: AExpr): nullable MType do return self.visit_expr_subtype(nexpr, self.type_bool(nexpr)) @@ -151,8 +189,6 @@ private class TypeVisitor var sup = self.resolve_mtype(ntype) if sup == null then return null # Forward error - var mmodule = self.nclassdef.mclassdef.mmodule - var anchor = self.nclassdef.mclassdef.bound_mtype if sup == sub then self.modelbuilder.warning(node, "Warning: Expression is already a {sup}.") else if self.is_subtype(sub, sup) and not sup.need_anchor then @@ -163,17 +199,22 @@ private class TypeVisitor fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty do - return self.modelbuilder.try_get_mproperty_by_name2(anode, self.nclassdef.mclassdef.mmodule, mtype, name) + return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name) end fun resolve_mtype(node: AType): nullable MType do - return self.modelbuilder.resolve_mtype(self.nclassdef, node) + return self.modelbuilder.resolve_mtype(mmodule, mclassdef, node) + end + + fun try_get_mclass(node: ANode, name: String): nullable MClass + do + var mclass = modelbuilder.try_get_mclass_by_name(node, mmodule, name) + return mclass end fun get_mclass(node: ANode, name: String): nullable MClass do - var mmodule = self.nclassdef.mclassdef.mmodule 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}'.") @@ -188,11 +229,15 @@ private class TypeVisitor return mclass.mclass_type end - fun get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable MMethodDef + fun get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite do var unsafe_type = self.anchor_to(recvtype) #debug("recv: {recvtype} (aka {unsafe_type})") + if recvtype isa MNullType then + self.error(node, "Error: Method '{name}' call on 'null'.") + return null + end var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) if mproperty == null then @@ -205,18 +250,51 @@ private class TypeVisitor return null end + assert mproperty isa MMethod + + if is_toplevel_context and recv_is_self and not mproperty.is_toplevel and name != "sys" and name != "exit" then + # FIXME named methods are here as a workaround + error(node, "Error: '{name}' is not a top-level method, thus need a receiver.") + end + if not recv_is_self and mproperty.is_toplevel then + error(node, "Error: cannot call '{name}', a top-level method, with a receiver.") + end + + if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then + self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self.") + return null + end + var propdefs = mproperty.lookup_definitions(self.mmodule, unsafe_type) + var mpropdef if propdefs.length == 0 then self.modelbuilder.error(node, "Type error: no definition found for property {name} in {unsafe_type}") return null - else if propdefs.length > 1 then - self.modelbuilder.error(node, "Error: confliting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}") - return null + else if propdefs.length == 1 then + mpropdef = propdefs.first + else + self.modelbuilder.warning(node, "Warning: confliting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}") + mpropdef = mproperty.intro end - var propdef = propdefs.first - assert propdef isa MMethodDef - return propdef + + var msignature = mpropdef.msignature.as(not null) + 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 + if rettype isa MParameterType then + var erased_rettype = msignature.return_mtype + assert erased_rettype != null + #node.debug("Erasure cast: Really a {rettype} but unsafely a {erased_rettype}") + erasure_cast = true + end + end + + var callsite = new CallSite(node, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) + return callsite end # Visit the expressions of args and cheik their conformity with the corresponding typi in signature @@ -245,12 +323,12 @@ private class TypeVisitor if i > vararg_rank then j = i + vararg_decl end - var paramtype = msignature.parameter_mtypes[i] + var paramtype = msignature.mparameters[i].mtype self.visit_expr_subtype(args[j], paramtype) end if vararg_rank >= 0 then var varargs = new Array[AExpr] - var paramtype = msignature.parameter_mtypes[vararg_rank] + 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) @@ -322,6 +400,44 @@ private class TypeVisitor end end +# A specific method call site with its associated informations. +class CallSite + # The assiciated node for location + var node: ANode + + # The static type of the receiver (possibly unresolved) + var recv: MType + + # The module where the callsite is present + var mmodule: MModule + + # The anchor to use with `recv` or `msignature` + var anchor: nullable MClassType + + # Is the receiver self? + # If "for_self", virtual types of the signature are keeped + # If "not_for_self", virtual type are erased + var recv_is_self: Bool + + # The designated method + var mproperty: MMethod + + # The statically designated method definition + # The most specif one, it is. + var mpropdef: MMethodDef + + # The resolved signature for the receiver + var msignature: MSignature + + # Is a implicit cast required on erasure typing policy? + var erasure_cast: Bool + + private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool + do + return v.check_signature(self.node, args, self.mproperty.name, self.msignature) + end +end + redef class Variable # The declared type of the variable var declared_type: nullable MType @@ -382,20 +498,19 @@ redef class APropdef var selfvariable: nullable Variable end -redef class AConcreteMethPropdef +redef class AMethPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var nclassdef = self.parent.as(AClassdef) - var mpropdef = self.mpropdef.as(not null) - var v = new TypeVisitor(modelbuilder, nclassdef, mpropdef) - self.selfvariable = v.selfvariable - var nblock = self.n_block if nblock == null then return + var mpropdef = self.mpropdef.as(not null) + 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.parameter_mtypes[i] + var mtype = mmethoddef.msignature.mparameters[i].mtype if mmethoddef.msignature.vararg_rank == i then var arrayclass = v.get_mclass(self.n_signature.n_params[i], "Array") if arrayclass == null then return # Skip error @@ -417,8 +532,8 @@ end redef class AAttrPropdef redef fun do_typing(modelbuilder: ModelBuilder) do - var nclassdef = self.parent.as(AClassdef) - var v = new TypeVisitor(modelbuilder, nclassdef, self.mpropdef.as(not null)) + var mpropdef = self.mpropdef.as(not null) + var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef) self.selfvariable = v.selfvariable var nexpr = self.n_expr @@ -433,13 +548,19 @@ end redef class AExpr # The static type of the expression. - # null if self is a statement of in case of error + # null if self is a statement or in case of error var mtype: nullable MType = null # Is the statement correctly typed? - # Used to distinguish errors and statements when `mtype' == null + # Used to distinguish errors and statements when `mtype == null` var is_typed: Bool = false + # If required, the following implicit cast `.as(XXX)` + # Such a cast may by required after evaluating the expression when + # a unsafe operation is detected (silently accepted by the Nit language). + # The attribute is computed by `check_subtype` + var implicit_cast_to: nullable MType = null + # Return the variable read (if any) # Used to perform adaptive typing fun its_variable: nullable Variable do return null @@ -456,6 +577,13 @@ redef class ABlockExpr for e in self.n_expr do v.visit_stmt(e) self.is_typed = true end + + # The type of a blockexpr is the one of the last expression (or null if empty) + redef fun mtype + do + if self.n_expr.is_empty then return null + return self.n_expr.last.mtype + end end redef class AVardeclExpr @@ -483,11 +611,13 @@ redef class AVardeclExpr end end - if mtype == null then - mtype = v.get_mclass(self, "Object").mclass_type.as_nullable + var decltype = mtype + if mtype == null or mtype isa MNullType then + decltype = v.get_mclass(self, "Object").mclass_type.as_nullable + if mtype == null then mtype = decltype end - variable.declared_type = mtype + variable.declared_type = decltype v.set_variable(self, variable, mtype) #debug("var {variable}: {mtype}") @@ -530,14 +660,14 @@ end redef class AReassignFormExpr # The method designed by the reassign operator. - var reassign_property: nullable MMethodDef = null + var reassign_callsite: nullable CallSite var read_type: nullable MType = null - # Determine the `reassign_property' - # `readtype' is the type of the reading of the left value. - # `writetype' is the type of the writing of the left value. - # (Because of ACallReassignExpr, both can be different. + # Determine the `reassign_property` + # `readtype` is the type of the reading of the left value. + # `writetype` is the type of the writing of the left value. + # (Because of `ACallReassignExpr`, both can be different. # Return the static type of the value to store. private fun resolve_reassignment(v: TypeVisitor, readtype, writetype: MType): nullable MType do @@ -557,19 +687,15 @@ redef class AReassignFormExpr return null end - var mpropdef = v.get_method(self, readtype, reassign_name, false) - if mpropdef == null then return null # Skip error - - self.reassign_property = mpropdef - - var msignature = mpropdef.msignature - assert msignature!= null - msignature = v.resolve_signature_for(msignature, readtype, false) + var callsite = v.get_method(self, readtype, reassign_name, false) + if callsite == null then return null # Skip error + self.reassign_callsite = callsite + var msignature = callsite.msignature var rettype = msignature.return_mtype assert msignature.arity == 1 and rettype != null - var value_type = v.visit_expr_subtype(self.n_value, msignature.parameter_mtypes.first) + var value_type = v.visit_expr_subtype(self.n_value, msignature.mparameters.first.mtype) if value_type == null then return null # Skip error v.check_subtype(self, rettype, writetype) @@ -586,6 +712,8 @@ redef class AVarReassignExpr var readtype = v.get_variable(self, variable) if readtype == null then return + read_type = readtype + var writetype = variable.declared_type if writetype == null then return @@ -704,28 +832,121 @@ redef class ALoopExpr end redef class AForExpr - redef fun accept_typing(v) + var coltype: nullable MClassType + + var method_iterator: nullable CallSite + var method_is_ok: nullable CallSite + var method_item: nullable CallSite + var method_next: nullable CallSite + var method_key: nullable CallSite + + private fun do_type_iterator(v: TypeVisitor, mtype: MType) do - var mtype = v.visit_expr(n_expr) - if mtype == null then return + if mtype isa MNullType then + v.error(self, "Type error: 'for' cannot iterate over 'null'") + return + end - var colcla = v.get_mclass(self, "Collection") - if colcla == null then return + # get obj class var objcla = v.get_mclass(self, "Object") if objcla == null then return - if v.is_subtype(mtype, colcla.get_mtype([objcla.mclass_type.as_nullable])) then - var coltype = mtype.supertype_to(v.mmodule, v.anchor, colcla) - assert coltype isa MGenericType + + # check iterator method + var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr) + if itdef == null then + v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.") + return + end + self.method_iterator = itdef + + # check that iterator return something + var ittype = itdef.msignature.return_mtype + if ittype == null then + v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.") + return + end + + # get iterator type + var colit_cla = v.try_get_mclass(self, "Iterator") + var mapit_cla = v.try_get_mclass(self, "MapIterator") + var is_col = false + var is_map = false + + if colit_cla != null and v.is_subtype(ittype, colit_cla.get_mtype([objcla.mclass_type.as_nullable])) then + # Iterator + var coltype = ittype.supertype_to(v.mmodule, v.anchor, colit_cla) var variables = self.variables if variables.length != 1 then - v.error(self, "Type Error: Expected one variable") + v.error(self, "Type Error: 'for' expects only one variable when using 'Iterator'.") else variables.first.declared_type = coltype.arguments.first end - else - v.modelbuilder.error(self, "TODO: Do 'for' on {mtype}") + 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 + # Map Iterator + var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla) + var variables = self.variables + if variables.length != 2 then + v.error(self, "Type Error: 'for' expects two variables when using 'MapIterator'.") + else + variables[0].declared_type = coltype.arguments[0] + variables[1].declared_type = coltype.arguments[1] + end + is_map = true end + if not is_col and not is_map then + v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.") + return + end + + # anchor formal and virtual types + if mtype.need_anchor then mtype = v.anchor_to(mtype) + + mtype = mtype.as_notnullable + self.coltype = mtype.as(MClassType) + + # get methods is_ok, next, item + var ikdef = v.get_method(self, ittype, "is_ok", false) + if ikdef == null then + v.error(self, "Type Error: 'for' expects a method 'is_ok' in 'Iterator' type {ittype}.") + return + end + self.method_is_ok = ikdef + + var itemdef = v.get_method(self, ittype, "item", false) + if itemdef == null then + v.error(self, "Type Error: 'for' expects a method 'item' in 'Iterator' type {ittype}.") + return + end + self.method_item = itemdef + + var nextdef = v.get_method(self, ittype, "next", false) + if nextdef == null then + v.error(self, "Type Error: 'for' expects a method 'next' in 'Iterator' type {ittype}.") + return + end + self.method_next = nextdef + + if is_map then + var keydef = v.get_method(self, ittype, "key", false) + if keydef == null then + v.error(self, "Type Error: 'for' expects a method 'key' in 'Iterator' type {ittype}.") + return + end + self.method_key = keydef + end + end + + redef fun accept_typing(v) + do + var mtype = v.visit_expr(n_expr) + if mtype == null then return + + self.do_type_iterator(v, mtype) + v.visit_stmt(n_block) self.is_typed = true end @@ -750,6 +971,15 @@ redef class AOrExpr end end +redef class AImpliesExpr + redef fun accept_typing(v) + do + v.visit_expr_bool(n_expr) + v.visit_expr_bool(n_expr2) + self.mtype = v.type_bool(self) + end +end + redef class AAndExpr redef fun accept_typing(v) do @@ -778,27 +1008,20 @@ redef class AOrElseExpr return # Skip error end - if t1 isa MNullableType then - t1 = t1.mtype - end + t1 = t1.as_notnullable var t = v.merge_types(self, [t1, t2]) if t == null then - v.error(self, "Type Error: ambiguous type {t1} vs {t2}") + t = v.mmodule.object_type + if t2 isa MNullableType then + t = t.as_nullable + end + #v.error(self, "Type Error: ambiguous type {t1} vs {t2}") end self.mtype = t end end -redef class AEeExpr - redef fun accept_typing(v) - do - v.visit_expr(n_expr) - v.visit_expr(n_expr2) - self.mtype = v.type_bool(self) - end -end - redef class ATrueExpr redef fun accept_typing(v) do @@ -856,12 +1079,15 @@ redef class ASuperstringExpr if mclass == null then return # Forward error self.mtype = mclass.mclass_type for nexpr in self.n_exprs do - var t = v.visit_expr(nexpr) + v.visit_expr_subtype(nexpr, v.mmodule.object_type) end end end redef class AArrayExpr + var with_capacity_callsite: nullable CallSite + var push_callsite: nullable CallSite + redef fun accept_typing(v) do var mtypes = new Array[nullable MType] @@ -879,25 +1105,50 @@ redef class AArrayExpr end var mclass = v.get_mclass(self, "Array") if mclass == null then return # Forward error - self.mtype = mclass.get_mtype([mtype]) + var array_mtype = mclass.get_mtype([mtype]) + + with_capacity_callsite = v.get_method(self, array_mtype, "with_capacity", false) + push_callsite = v.get_method(self, array_mtype, "push", false) + + self.mtype = array_mtype end end redef class ARangeExpr + var init_callsite: nullable CallSite + redef fun accept_typing(v) do - var t1 = v.visit_expr(self.n_expr) - var t2 = v.visit_expr(self.n_expr2) + var discrete_class = v.get_mclass(self, "Discrete") + if discrete_class == null then return # Forward error + var discrete_type = discrete_class.intro.bound_mtype + var t1 = v.visit_expr_subtype(self.n_expr, discrete_type) + var t2 = v.visit_expr_subtype(self.n_expr2, discrete_type) if t1 == null or t2 == null then return var mclass = v.get_mclass(self, "Range") if mclass == null then return # Forward error + var mtype if v.is_subtype(t1, t2) then - self.mtype = mclass.get_mtype([t2]) + mtype = mclass.get_mtype([t2]) else if v.is_subtype(t2, t1) then - self.mtype = mclass.get_mtype([t1]) + mtype = mclass.get_mtype([t1]) else v.error(self, "Type Error: Cannot create range: {t1} vs {t2}") + return + end + + self.mtype = mtype + + # get the constructor + var callsite + if self isa ACrangeExpr then + callsite = v.get_method(self, mtype, "init", false) + else if self isa AOrangeExpr then + callsite = v.get_method(self, mtype, "without_last", false) + else + abort end + init_callsite = callsite end end @@ -910,7 +1161,7 @@ end redef class AIsaExpr # The static type to cast to. - # (different from the static type of the expression that is Bool). + # (different from the static type of the expression that is `Bool`). var cast_type: nullable MType redef fun accept_typing(v) do @@ -941,6 +1192,8 @@ redef class AAsNotnullExpr redef fun accept_typing(v) do var mtype = v.visit_expr(self.n_expr) + if mtype == null then return # Forward error + if mtype isa MNullType then v.error(self, "Type error: as(not null) on null") return @@ -949,8 +1202,18 @@ redef class AAsNotnullExpr self.mtype = mtype.mtype return end - # TODO: warn on useless as not null self.mtype = mtype + + if mtype isa MClassType then + v.modelbuilder.warning(self, "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, "Warning: expression is already not null, since it is a `{mtype}: {u}`.") + return + end end end @@ -965,6 +1228,9 @@ redef class ASelfExpr redef var its_variable: nullable Variable redef fun accept_typing(v) do + if v.is_toplevel_context and not self isa AImplicitSelfExpr then + v.error(self, "Error: self cannot be used in top-level method.") + end var variable = v.selfvariable self.its_variable = variable self.mtype = v.get_variable(self, variable) @@ -975,7 +1241,7 @@ end redef class ASendExpr # The property invoked by the send. - var mproperty: nullable MMethod + var callsite: nullable CallSite redef fun accept_typing(v) do @@ -988,21 +1254,16 @@ redef class ASendExpr return end - var propdef = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr) - if propdef == null then return - var mproperty = propdef.mproperty - self.mproperty = mproperty - var msignature = propdef.msignature - if msignature == null then abort # Forward error - - var for_self = self.n_expr isa ASelfExpr - msignature = v.resolve_signature_for(msignature, recvtype, for_self) + 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 var args = compute_raw_arguments - v.check_signature(self, args, name, msignature) + callsite.check_signature(v, args) - if mproperty.is_init then + if callsite.mproperty.is_init then var vmpropdef = v.mpropdef if not (vmpropdef isa MMethodDef and vmpropdef.mproperty.is_init) then v.error(self, "Can call a init only in another init") @@ -1022,7 +1283,9 @@ redef class ASendExpr private fun property_name: String is abstract # An array of all arguments (excluding self) - fun compute_raw_arguments: Array[AExpr] is abstract + fun raw_arguments: Array[AExpr] do return compute_raw_arguments + + private fun compute_raw_arguments: Array[AExpr] is abstract end redef class ABinopExpr @@ -1137,7 +1400,7 @@ end redef class ASendReassignFormExpr # The property invoked for the writing - var write_mproperty: nullable MMethod = null + var write_callsite: nullable CallSite redef fun accept_typing(v) do @@ -1150,38 +1413,32 @@ redef class ASendReassignFormExpr return end - var propdef = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr) - if propdef == null then return - var mproperty = propdef.mproperty - self.mproperty = mproperty - var msignature = propdef.msignature - if msignature == null then abort # Forward error var for_self = self.n_expr isa ASelfExpr - msignature = v.resolve_signature_for(msignature, recvtype, for_self) + var callsite = v.get_method(self, recvtype, name, for_self) + + if callsite == null then return + self.callsite = callsite var args = compute_raw_arguments - v.check_signature(self, args, name, msignature) + callsite.check_signature(v, args) - var readtype = msignature.return_mtype + var readtype = callsite.msignature.return_mtype if readtype == null then v.error(self, "Error: {name} is not a function") return end - var wpropdef = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr) - if wpropdef == null then return - var wmproperty = wpropdef.mproperty - self.write_mproperty = wmproperty - var wmsignature = wpropdef.msignature - if wmsignature == null then abort # Forward error - wmsignature = v.resolve_signature_for(wmsignature, recvtype, for_self) + var wcallsite = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr) + if wcallsite == null then return + self.write_callsite = wcallsite - var wtype = self.resolve_reassignment(v, readtype, wmsignature.parameter_mtypes.last) + var wtype = self.resolve_reassignment(v, readtype, wcallsite.msignature.mparameters.last.mtype) if wtype == null then return + args = args.to_a # duplicate so raw_arguments keeps only the getter args args.add(self.n_value) - v.check_signature(self, args, name + "=", wmsignature) + wcallsite.check_signature(v, args) self.is_typed = true end @@ -1211,11 +1468,16 @@ end redef class ASuperExpr # The method to call if the super is in fact a 'super init call' # Note: if the super is a normal call-next-method, then this attribute is null - var mproperty: nullable MMethod + var callsite: nullable CallSite + + # The method to call is the super is a standard `call-next-method` super-call + # Note: if the super is a special super-init-call, then this attribute is null + var mpropdef: nullable MMethodDef redef fun accept_typing(v) do - var recvtype = v.nclassdef.mclassdef.bound_mtype + var recvtype = v.anchor + assert recvtype != null var mproperty = v.mpropdef.mproperty if not mproperty isa MMethod then v.error(self, "Error: super only usable in a method") @@ -1229,28 +1491,31 @@ redef class ASuperExpr end v.error(self, "Error: No super method to call for {mproperty}.") return - else if superprops.length > 1 then - v.modelbuilder.warning(self, "Error: Conflicting super method to call for {mproperty}: {superprops.join(", ")}.") - return end + # FIXME: covariance of return type in linear extension? var superprop = superprops.first - assert superprop isa MMethodDef var msignature = superprop.msignature.as(not null) - msignature = v.resolve_signature_for(msignature, recvtype, true) + msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) var args = self.n_args.to_a if args.length > 0 then v.check_signature(self, args, mproperty.name, msignature) end self.mtype = msignature.return_mtype + self.is_typed = true + v.mpropdef.has_supercall = true + mpropdef = v.mpropdef.as(MMethodDef) end private fun process_superinit(v: TypeVisitor) do - var recvtype = v.nclassdef.mclassdef.bound_mtype - var mproperty = v.mpropdef.mproperty + var recvtype = v.anchor + assert recvtype != null + var mpropdef = v.mpropdef + assert mpropdef isa MMethodDef + var mproperty = mpropdef.mproperty var superprop: nullable MMethodDef = null - for msupertype in v.nclassdef.mclassdef.supertypes do + for msupertype in mpropdef.mclassdef.supertypes do msupertype = msupertype.anchor_to(v.mmodule, recvtype) var errcount = v.modelbuilder.toolcontext.error_count var candidate = v.try_get_mproperty_by_name2(self, msupertype, mproperty.name).as(nullable MMethod) @@ -1277,15 +1542,32 @@ redef class ASuperExpr v.error(self, "Error: No super method to call for {mproperty}.") return end - self.mproperty = superprop.mproperty - var args = self.n_args.to_a var msignature = superprop.msignature.as(not null) - msignature = v.resolve_signature_for(msignature, recvtype, true) + msignature = v.resolve_for(msignature, recvtype, true).as(MSignature) + + var callsite = new CallSite(self, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false) + self.callsite = callsite + + var args = self.n_args.to_a if args.length > 0 then - v.check_signature(self, args, mproperty.name, msignature) + callsite.check_signature(v, args) else - # TODO: Check signature + # Check there is at least enough parameters + if mpropdef.msignature.arity < msignature.arity then + v.error(self, "Error: Not enough implicit arguments to pass. Got {mpropdef.msignature.arity}, expected at least {msignature.arity}. Signature is {msignature}") + return + end + # Check that each needed parameter is conform + var i = 0 + for sp in msignature.mparameters do + var p = mpropdef.msignature.mparameters[i] + if not v.is_subtype(p.mtype, sp.mtype) then + v.error(self, "Type error: expected argument #{i} of type {sp.mtype}, got implicit argument {p.name} of type {p.mtype}. Signature is {msignature}") + return + end + i += 1 + end end self.is_typed = true @@ -1296,7 +1578,7 @@ end redef class ANewExpr # The constructor invoked by the new. - var mproperty: nullable MMethod + var callsite: nullable CallSite redef fun accept_typing(v) do @@ -1312,6 +1594,14 @@ 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 var name: String @@ -1321,21 +1611,18 @@ redef class ANewExpr else name = "init" end - var propdef = v.get_method(self, recvtype, name, false) - if propdef == null then return + var callsite = v.get_method(self, recvtype, name, false) + if callsite == null then return - self.mproperty = propdef.mproperty + self.callsite = callsite - if not propdef.mproperty.is_init_for(recvtype.mclass) then + if not callsite.mproperty.is_init_for(recvtype.mclass) then v.error(self, "Error: {name} is not a constructor.") return end - var msignature = propdef.msignature.as(not null) - msignature = v.resolve_signature_for(msignature, recvtype, false) - var args = n_args.to_a - v.check_signature(self, args, name, msignature) + callsite.check_signature(v, args) end end @@ -1428,15 +1715,6 @@ end ### -redef class AClosureCallExpr - redef fun accept_typing(v) - do - #TODO - end -end - -### - redef class ADebugTypeExpr redef fun accept_typing(v) do @@ -1449,5 +1727,6 @@ redef class ADebugTypeExpr var umtype = v.anchor_to(mtype) v.modelbuilder.warning(self, "Found type {expr} (-> {unsafe}), expected {mtype} (-> {umtype})") end + self.is_typed = true end end