X-Git-Url: http://nitlanguage.org diff --git a/src/typing.nit b/src/typing.nit index fb169f8..719fae2 100644 --- a/src/typing.nit +++ b/src/typing.nit @@ -19,7 +19,23 @@ 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 + +redef class MPropDef + # Does the MPropDef contains a call to super or a call of a super-constructor? + var has_supercall: Bool = false +end + +private class TypingPhase + super Phase + redef fun process_npropdef(npropdef) do npropdef.do_typing(toolcontext.modelbuilder) +end private class TypeVisitor var modelbuilder: ModelBuilder @@ -68,7 +84,7 @@ private class TypeVisitor return res end - # Retrieve the signature of a MMethodDef resolved for a specific call. + # Retrieve the signature of a `MMethodDef` resolved for a specific call. # This method is an helper to symplify the query on the model. # # Note: `for_self` indicates if the reciever is self or not. @@ -81,7 +97,7 @@ private class TypeVisitor # 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 `sun` is an insafe subtype (ie an imlicit cast is required), then return `sup`. + # 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`. @@ -109,7 +125,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) @@ -127,11 +143,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) @@ -146,11 +162,11 @@ private class TypeVisitor 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)) @@ -237,19 +253,33 @@ private class TypeVisitor 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 mpropdef = propdefs.first var msignature = self.resolve_signature_for(mpropdef, recvtype, recv_is_self) - var callsite = new CallSite(node, recvtype, recv_is_self, mproperty, mpropdef, msignature) + var erasure_cast = false + var rettype = mpropdef.msignature.return_mtype + if not recv_is_self and rettype != null then + if rettype isa MNullableType then rettype = rettype.mtype + 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, recv_is_self, mproperty, mpropdef, msignature, erasure_cast) return callsite end @@ -379,6 +409,9 @@ class CallSite # 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) @@ -468,12 +501,6 @@ redef class AConcreteMethPropdef assert variable != null variable.declared_type = mtype end - for i in [0..mmethoddef.msignature.mclosures.length[ do - var mclosure = mmethoddef.msignature.mclosures[i] - var variable = self.n_signature.n_closure_decls[i].variable - assert variable != null - variable.declared_type = mclosure.mtype - end v.visit_stmt(nblock) if not nblock.after_flow_context.is_unreachable and mmethoddef.msignature.return_mtype != null then @@ -506,10 +533,10 @@ redef class AExpr 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)" + # 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` @@ -531,6 +558,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 @@ -558,11 +592,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}") @@ -605,14 +641,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 @@ -634,9 +670,7 @@ redef class AReassignFormExpr var callsite = v.get_method(self, readtype, reassign_name, false) if callsite == null then return null # Skip error - var mpropdef = callsite.mpropdef - - self.reassign_property = mpropdef + self.reassign_callsite = callsite var msignature = callsite.msignature var rettype = msignature.return_mtype @@ -659,6 +693,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 @@ -787,16 +823,44 @@ redef class AForExpr private fun do_type_iterator(v: TypeVisitor, mtype: MType) do + # get obj class var objcla = v.get_mclass(self, "Object") if objcla == null then return + # check iterator method + var unsafe_type = v.anchor_to(mtype) + if v.try_get_mproperty_by_name2(self, unsafe_type, "iterator") == null then + if v.try_get_mproperty_by_name2(self, unsafe_type, "iterate") == null then + v.error(self, "Type Error: Expected method 'iterator' in type {mtype}") + else + v.modelbuilder.error(self, "NOT YET IMPLEMENTED: Do 'for' on {mtype}") + end + return + end + + var itdef = v.get_method(self, mtype, "iterator", true) + if itdef == null then + v.error(self, "Type Error: Expected method 'iterator' in type {mtype}") + return + end + self.method_iterator = itdef.mproperty + + # check that iterator return something + var ittype = itdef.msignature.return_mtype + if ittype == null then + v.error(self, "Type Error: Expected 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 - var colcla = v.try_get_mclass(self, "Collection") - if colcla != null and v.is_subtype(mtype, colcla.get_mtype([objcla.mclass_type.as_nullable])) then - var coltype = mtype.supertype_to(v.mmodule, v.anchor, colcla) - self.coltype = coltype + 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") @@ -806,10 +870,9 @@ redef class AForExpr is_col = true end - var mapcla = v.try_get_mclass(self, "Map") - if mapcla != null and v.is_subtype(mtype, mapcla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then - var coltype = mtype.supertype_to(v.mmodule, v.anchor, mapcla) - self.coltype = coltype + 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: Expected two variables") @@ -820,57 +883,46 @@ redef class AForExpr is_map = true end - if is_col or is_map then - # get iterator method - var coltype = self.coltype.as(not null) - var itdef = v.get_method(self, coltype, "iterator", true) - if itdef == null then - v.error(self, "Type Error: Expected method 'iterator' in type {coltype}") - return - end - self.method_iterator = itdef.mproperty + if not is_col and not is_map then + v.error(self, "Type Error: Expected method 'iterator' to return an Iterator of MapIterator type") + return + end - # get iterator type - var ittype = itdef.msignature.return_mtype - if ittype == null then - v.error(self, "Type Error: Expected method 'iterator' to return an Iterator type") - return - end + # anchor formal and virtual types + if mtype.need_anchor then mtype = v.anchor_to(mtype) - # 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: Expected method 'is_ok' in Iterator type {ittype}") - return - end - self.method_is_ok = ikdef.mproperty + self.coltype = mtype.as(MClassType) - var itemdef = v.get_method(self, ittype, "item", false) - if itemdef == null then - v.error(self, "Type Error: Expected method 'item' in Iterator type {ittype}") - return - end - self.method_item = itemdef.mproperty + # 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: Expected method 'is_ok' in Iterator type {ittype}") + return + end + self.method_is_ok = ikdef.mproperty - var nextdef = v.get_method(self, ittype, "next", false) - if nextdef == null then - v.error(self, "Type Error: Expected method 'next' in Iterator type {ittype}") - return - end - self.method_next = nextdef.mproperty + var itemdef = v.get_method(self, ittype, "item", false) + if itemdef == null then + v.error(self, "Type Error: Expected method 'item' in Iterator type {ittype}") + return + end + self.method_item = itemdef.mproperty - if is_map then - var keydef = v.get_method(self, ittype, "key", false) - if keydef == null then - v.error(self, "Type Error: Expected method 'key' in Iterator type {ittype}") - return - end - self.method_key = keydef.mproperty - end + var nextdef = v.get_method(self, ittype, "next", false) + if nextdef == null then + v.error(self, "Type Error: Expected method 'next' in Iterator type {ittype}") return end + self.method_next = nextdef.mproperty - v.modelbuilder.error(self, "NOT YET IMPLEMENTED: Do 'for' on {mtype}") + if is_map then + var keydef = v.get_method(self, ittype, "key", false) + if keydef == null then + v.error(self, "Type Error: Expected method 'key' in Iterator type {ittype}") + return + end + self.method_key = keydef.mproperty + end end redef fun accept_typing(v) @@ -904,6 +956,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 @@ -938,21 +999,16 @@ redef class AOrElseExpr 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 @@ -1067,7 +1123,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 @@ -1132,7 +1188,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 @@ -1147,8 +1203,7 @@ redef class ASendExpr var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr) if callsite == null then return - var mproperty = callsite.mproperty - self.mproperty = mproperty + self.callsite = callsite var msignature = callsite.msignature var args = compute_raw_arguments @@ -1156,7 +1211,7 @@ redef class ASendExpr 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") @@ -1169,14 +1224,6 @@ redef class ASendExpr else self.is_typed = true end - - if self.n_closure_defs.length == msignature.mclosures.length then - for i in [0..self.n_closure_defs.length[ do - self.n_closure_defs[i].accept_typing(v, msignature.mclosures[i]) - end - else - debug("closure: got {self.n_closure_defs.length}, want {msignature.mclosures.length}") - end end # The name of the property @@ -1301,7 +1348,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 @@ -1318,8 +1365,7 @@ redef class ASendReassignFormExpr var callsite = v.get_method(self, recvtype, name, for_self) if callsite == null then return - var mproperty = callsite.mproperty - self.mproperty = mproperty + self.callsite = callsite var args = compute_raw_arguments self.raw_arguments = args @@ -1334,8 +1380,7 @@ redef class ASendReassignFormExpr var wcallsite = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr) if wcallsite == null then return - var wmproperty = wcallsite.mproperty - self.write_mproperty = wmproperty + self.write_callsite = wcallsite var wtype = self.resolve_reassignment(v, readtype, wcallsite.msignature.mparameters.last.mtype) if wtype == null then return @@ -1372,7 +1417,7 @@ 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 redef fun accept_typing(v) do @@ -1390,12 +1435,9 @@ 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, "Warning: NOT YET IMPLEMENTED: 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 = v.resolve_signature_for(superprop, recvtype, true) var args = self.n_args.to_a @@ -1403,6 +1445,8 @@ redef class ASuperExpr v.check_signature(self, args, mproperty.name, msignature) end self.mtype = msignature.return_mtype + self.is_typed = true + v.mpropdef.has_supercall = true end private fun process_superinit(v: TypeVisitor) @@ -1437,12 +1481,14 @@ 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 = v.resolve_signature_for(superprop, recvtype, true) + var callsite = new CallSite(self, recvtype, 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 end @@ -1455,7 +1501,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 @@ -1471,6 +1517,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 @@ -1483,7 +1537,7 @@ redef class ANewExpr var callsite = v.get_method(self, recvtype, name, false) if callsite == null then return - self.mproperty = callsite.mproperty + self.callsite = callsite if not callsite.mproperty.is_init_for(recvtype.mclass) then v.error(self, "Error: {name} is not a constructor.") @@ -1584,50 +1638,6 @@ end ### -redef class AClosureCallExpr - redef fun accept_typing(v) - do - var variable = self.variable - if variable == null then return # Skip error - - var recvtype = v.nclassdef.mclassdef.bound_mtype - var msignature = variable.declared_type.as(not null) - msignature = v.resolve_for(msignature, recvtype, false).as(MSignature) - - var args = n_args.to_a - v.check_signature(self, args, variable.name, msignature) - - self.is_typed = true - self.mtype = msignature.return_mtype - end -end - -redef class AClosureDef - var mclosure: nullable MParameter - - private fun accept_typing(v: TypeVisitor, mparameter: MParameter) - do - var variables = self.variables - if variables == null then return - - self.mclosure = mparameter - var msignature = mparameter.mtype.as(MSignature) - - if msignature.arity != variables.length then - v.error(self, "Type error: closure {mparameter.name} expects {msignature.arity} parameters, {variables.length} given") - return - end - - for i in [0..variables.length[ do - variables[i].declared_type = msignature.mparameters[i].mtype - end - - v.visit_stmt(self.n_expr) - end -end - -### - redef class ADebugTypeExpr redef fun accept_typing(v) do @@ -1640,5 +1650,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