X-Git-Url: http://nitlanguage.org diff --git a/src/modelbuilder_base.nit b/src/modelbuilder_base.nit index 1f8c184..8e1e7c6 100644 --- a/src/modelbuilder_base.nit +++ b/src/modelbuilder_base.nit @@ -74,13 +74,23 @@ class ModelBuilder if res == null then res = mclass else - error(anode, "Ambigous class name '{name}'; conflict between {mclass.full_name} and {res.full_name}") + error(anode, "Error: ambiguous class name `{name}`; conflict between `{mclass.full_name}` and `{res.full_name}`.") return null end end return res end + # Like `try_get_mclass_by_name` but display an error message when the class is not found + fun get_mclass_by_name(node: ANode, mmodule: MModule, name: String): nullable MClass + do + var mclass = try_get_mclass_by_name(node, mmodule, name) + if mclass == null then + error(node, "Type Error: missing primitive class `{name}'.") + end + return mclass + end + # Return a property named `name` on the type `mtype` visible in the module `mmodule`. # Visibility in modules is correctly handled. # Protected properties are returned (it is up to the caller to check and reject protected properties). @@ -158,7 +168,7 @@ class ModelBuilder assert ress.length > 1 var s = new Array[String] for mprop in ress do s.add mprop.full_name - self.error(anode, "Ambigous property name '{name}' for {mtype}; conflict between {s.join(" and ")}") + self.error(anode, "Error: ambiguous property name `{name}` for `{mtype}`; conflict between {s.join(" and ")}.") end self.try_get_mproperty_by_name2_cache[mmodule, mtype, name] = res @@ -208,11 +218,148 @@ class ModelBuilder if res == null then var l = null if n != null then l = n.hot_location - self.toolcontext.fatal_error(l, "Fatal Error: {recv} must have a property named {name}.") + self.toolcontext.fatal_error(l, "Fatal Error: `{recv}` must have a property named `{name}`.") abort end return res end + + # Return the static type associated to the node `ntype`. + # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types) + # In case of problem, an error is displayed on `ntype` and null is returned. + # FIXME: the name "resolve_mtype" is awful + fun resolve_mtype_unchecked(mmodule: MModule, mclassdef: nullable MClassDef, ntype: AType, with_virtual: Bool): nullable MType + do + var name = ntype.n_id.text + var res: MType + + # Check virtual type + if mclassdef != null and with_virtual then + var prop = try_get_mproperty_by_name(ntype, mclassdef, name).as(nullable MVirtualTypeProp) + if prop != null then + if not ntype.n_types.is_empty then + error(ntype, "Type Error: formal type `{name}` cannot have formal parameters.") + end + res = prop.mvirtualtype + if ntype.n_kwnullable != null then res = res.as_nullable + ntype.mtype = res + return res + end + end + + # Check parameter type + if mclassdef != null then + for p in mclassdef.mclass.mparameters do + if p.name != name then continue + + if not ntype.n_types.is_empty then + error(ntype, "Type Error: formal type `{name}` cannot have formal parameters.") + end + + res = p + if ntype.n_kwnullable != null then res = res.as_nullable + ntype.mtype = res + return res + end + end + + # Check class + var mclass = try_get_mclass_by_name(ntype, mmodule, name) + if mclass != null then + var arity = ntype.n_types.length + if arity != mclass.arity then + if arity == 0 then + error(ntype, "Type Error: `{mclass.signature_to_s}` is a generic class.") + else if mclass.arity == 0 then + error(ntype, "Type Error: `{name}` is not a generic class.") + else + error(ntype, "Type Error: expected {mclass.arity} formal argument(s) for `{mclass.signature_to_s}`; got {arity}.") + end + return null + end + if arity == 0 then + res = mclass.mclass_type + if ntype.n_kwnullable != null then res = res.as_nullable + ntype.mtype = res + return res + else + var mtypes = new Array[MType] + for nt in ntype.n_types do + var mt = resolve_mtype_unchecked(mmodule, mclassdef, nt, with_virtual) + if mt == null then return null # Forward error + mtypes.add(mt) + end + res = mclass.get_mtype(mtypes) + if ntype.n_kwnullable != null then res = res.as_nullable + ntype.mtype = res + return res + end + end + + # If everything fail, then give up :( + error(ntype, "Error: class `{name}` not found in module `{mmodule}`.") + return null + end + + # Return the static type associated to the node `ntype`. + # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types) + # In case of problem, an error is displayed on `ntype` and null is returned. + # FIXME: the name "resolve_mtype" is awful + fun resolve_mtype(mmodule: MModule, mclassdef: nullable MClassDef, ntype: AType): nullable MType + do + var mtype = ntype.mtype + if mtype == null then mtype = resolve_mtype_unchecked(mmodule, mclassdef, ntype, true) + if mtype == null then return null # Forward error + + if ntype.checked_mtype then return mtype + if mtype isa MGenericType then + var mclass = mtype.mclass + for i in [0..mclass.arity[ do + var intro = mclass.try_intro + if intro == null then return null # skip error + var bound = intro.bound_mtype.arguments[i] + var nt = ntype.n_types[i] + var mt = resolve_mtype(mmodule, mclassdef, nt) + if mt == null then return null # forward error + var anchor + if mclassdef != null then anchor = mclassdef.bound_mtype else anchor = null + if not check_subtype(nt, mmodule, anchor, mt, bound) then + error(nt, "Type Error: expected `{bound}`, got `{mt}`.") + return null + end + end + end + ntype.checked_mtype = true + return mtype + end + + # Check that `sub` is a subtype of `sup`. + # Do not display an error message. + # + # This method is used a an entry point for the modelize phase to test static subtypes. + # Some refinements could redefine it to collect statictics. + fun check_subtype(node: ANode, mmodule: MModule, anchor: nullable MClassType, sub, sup: MType): Bool + do + return sub.is_subtype(mmodule, anchor, sup) + end + + # Check that `sub` and `sup` are equvalent types. + # Do not display an error message. + # + # This method is used a an entry point for the modelize phase to test static equivalent types. + # Some refinements could redefine it to collect statictics. + fun check_sametype(node: ANode, mmodule: MModule, anchor: nullable MClassType, sub, sup: MType): Bool + do + return sub.is_subtype(mmodule, anchor, sup) and sup.is_subtype(mmodule, anchor, sub) + end +end + +redef class AType + # The mtype associated to the node + var mtype: nullable MType = null + + # Is the mtype a valid one? + var checked_mtype: Bool = false end redef class AVisibility @@ -240,7 +387,7 @@ redef class ADoc do var res = mdoc_cache if res != null then return res - res = new MDoc + res = new MDoc(location) for c in n_comment do var text = c.text if text.length < 2 then