From: Jean Privat Date: Wed, 7 Oct 2015 14:02:59 +0000 (-0400) Subject: Merge: More robustness in keep going X-Git-Tag: v0.7.9~41 X-Git-Url: http://nitlanguage.org?hp=a662896faea407d32bb71a2b86742a5300bbe7d1 Merge: More robustness in keep going A pass on the various modules of `nitc` to make them more robust when `keep_going` is activated. The main changes are the introductions of the flags `is_broken` in the root classes ANode and MEntity. This flags simplify the clients code to check the validity of entities before working with them. Pull-Request: #1746 Reviewed-by: Alexis Laferrière Reviewed-by: Lucas Bajolet --- diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index b79e9ba..101ff83 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -1147,6 +1147,7 @@ abstract class AbstractCompilerVisitor fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do + if callsite.is_broken then return null var initializers = callsite.mpropdef.initializers if not initializers.is_empty then var recv = arguments.first @@ -1732,7 +1733,7 @@ abstract class AbstractCompilerVisitor fun stmt(nexpr: nullable AExpr) do if nexpr == null then return - if nexpr.mtype == null and not nexpr.is_typed then + if nexpr.is_broken then # Untyped expression. # Might mean dead code or invalid code # so aborts @@ -3650,6 +3651,7 @@ redef class ASendExpr do var recv = v.expr(self.n_expr, null) var callsite = self.callsite.as(not null) + if callsite.is_broken then return null var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments) return v.compile_callsite(callsite, args) end @@ -3660,6 +3662,7 @@ redef class ASendReassignFormExpr do var recv = v.expr(self.n_expr, null) var callsite = self.callsite.as(not null) + if callsite.is_broken then return var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments) var value = v.expr(self.n_value, null) @@ -3682,6 +3685,7 @@ redef class ASuperExpr var callsite = self.callsite if callsite != null then + if callsite.is_broken then return null var args if self.n_args.n_exprs.is_empty then @@ -3731,6 +3735,7 @@ redef class ANewExpr var callsite = self.callsite if callsite == null then return recv + if callsite.is_broken then return null var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs) var res2 = v.compile_callsite(callsite, args) @@ -3870,7 +3875,12 @@ end # Here we load an process all modules passed on the command line var mmodules = modelbuilder.parse(arguments) -if mmodules.is_empty then return +if mmodules.is_empty then + toolcontext.check_errors + toolcontext.errors_info + if toolcontext.error_count > 0 then exit(1) else exit(0) +end + modelbuilder.run_phases for mmodule in mmodules do diff --git a/src/compiler/java_compiler.nit b/src/compiler/java_compiler.nit index f40ce45..84b5a47 100644 --- a/src/compiler/java_compiler.nit +++ b/src/compiler/java_compiler.nit @@ -1330,7 +1330,7 @@ end redef class MClass # Runtime name - private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}" + private fun rt_name: String do return "RTClass_{intro_mmodule.jname}_{jname}" # Generate a Java RTClass for a Nit MClass fun compile_to_java(v: JavaCompilerVisitor) do diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index 00f3f00..d3390af 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -626,7 +626,7 @@ class SeparateCompiler for cd in mmodule.mclassdefs do for pd in cd.mpropdefs do if not pd isa MMethodDef then continue - if pd.msignature == null then continue # Skip broken method + if pd.mproperty.is_broken or pd.is_broken or pd.msignature == null then continue # Skip broken method var rta = runtime_type_analysis if modelbuilder.toolcontext.opt_skip_dead_methods.value and rta != null and not rta.live_methoddefs.has(pd) then continue #print "compile {pd} @ {cd} @ {mmodule}" @@ -814,6 +814,8 @@ class SeparateCompiler # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally fun compile_class_to_c(mclass: MClass) do + if mclass.is_broken then return + var mtype = mclass.intro.bound_mtype var c_name = mclass.c_name @@ -843,6 +845,9 @@ class SeparateCompiler if rta != null and not rta.live_methoddefs.has(mpropdef) then v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */") continue + else if mpropdef.is_broken or mpropdef.msignature == null or mpropdef.mproperty.is_broken then + v.add_decl("NULL, /* DEAD (BROKEN) {mclass.intro_mmodule}:{mclass}:{mpropdef} */") + continue end var rf = mpropdef.virtual_runtime_function v.require_declaration(rf.c_name) diff --git a/src/doc/doc_phases/doc_structure.nit b/src/doc/doc_phases/doc_structure.nit index f2686e6..d84a739 100644 --- a/src/doc/doc_phases/doc_structure.nit +++ b/src/doc/doc_phases/doc_structure.nit @@ -147,7 +147,7 @@ redef class MModulePage private fun mclasses_for_mmodule(mmodule: MModule): Set[MClass] do var mclasses = new HashSet[MClass] for mclass in self.mclasses do - if mclass.intro.mmodule == mmodule then + if mclass.intro_mmodule == mmodule then mclasses.add mclass end end diff --git a/src/ffi/extern_classes.nit b/src/ffi/extern_classes.nit index c0e826b..40459df 100644 --- a/src/ffi/extern_classes.nit +++ b/src/ffi/extern_classes.nit @@ -110,7 +110,7 @@ redef class MClass var ftype_b: nullable ForeignType = null # FIXME hack to circumvent bug where ftype is typed null # look in super classes - for s in in_hierarchy(intro.mmodule).direct_greaters do + for s in in_hierarchy(intro_mmodule).direct_greaters do var super_ftype = s.compute_ftype(v) if super_ftype != null then if ftype_b == null then diff --git a/src/highlight.nit b/src/highlight.nit index 00ed2bf..dd9ceff 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -373,7 +373,7 @@ redef class MClassDef res.new_field("class").text(mclass.name) else res.new_field("redef class").text(mclass.name) - res.new_field("intro").add mclass.intro.linkto_text("in {mclass.intro.mmodule.to_s}") + res.new_field("intro").add mclass.intro.linkto_text("in {mclass.intro_mmodule.to_s}") end var mdoc = self.mdoc if mdoc == null then mdoc = mclass.intro.mdoc @@ -584,7 +584,6 @@ redef class MSignature end redef class CallSite - super HInfoBoxable redef fun infobox(v) do var res = new HInfoBox(v, "call {mpropdef}") diff --git a/src/loader.nit b/src/loader.nit index a914b56..fb1801b 100644 --- a/src/loader.nit +++ b/src/loader.nit @@ -819,11 +819,13 @@ redef class ModelBuilder # Load the imported module var suppath = seach_module_by_amodule_name(aimport.n_name, mmodule.mgroup) if suppath == null then + mmodule.is_broken = true nmodule.mmodule = null # invalidate the module continue # Skip error end var sup = load_module_path(suppath) if sup == null then + mmodule.is_broken = true nmodule.mmodule = null # invalidate the module continue # Skip error end @@ -832,16 +834,20 @@ redef class ModelBuilder imported_modules.add(sup) var mvisibility = aimport.n_visibility.mvisibility if mvisibility == protected_visibility then + mmodule.is_broken = true error(aimport.n_visibility, "Error: only properties can be protected.") + mmodule.is_broken = true nmodule.mmodule = null # invalidate the module return end if sup == mmodule then error(aimport.n_name, "Error: dependency loop in module {mmodule}.") + mmodule.is_broken = true nmodule.mmodule = null # invalidate the module end if sup.in_importation < mmodule then error(aimport.n_name, "Error: dependency loop between modules {mmodule} and {sup}.") + mmodule.is_broken = true nmodule.mmodule = null # invalidate the module return end @@ -851,6 +857,7 @@ redef class ModelBuilder var mod_name = "core" var sup = self.get_mmodule_by_name(nmodule, null, mod_name) if sup == null then + mmodule.is_broken = true nmodule.mmodule = null # invalidate the module else # Skip error imported_modules.add(sup) diff --git a/src/model/model.nit b/src/model/model.nit index 8d6bf7a..941bae7 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -2301,7 +2301,7 @@ abstract class MPropDef redef var to_s: String is noinit # Is self the definition that introduce the property? - fun is_intro: Bool do return mproperty.intro == self + fun is_intro: Bool do return isset mproperty._intro and mproperty.intro == self # Return the next definition in linearization of `mtype`. # diff --git a/src/model/model_base.nit b/src/model/model_base.nit index 396d5e1..8b33f15 100644 --- a/src/model/model_base.nit +++ b/src/model/model_base.nit @@ -64,6 +64,20 @@ abstract class MEntity # A Model Entity has a direct link to its model fun model: Model is abstract + + # The indication that the entity did not pass some semantic verifications. + # + # This simple flag is set by a given analysis to say that the entity is broken and unusable in + # an execution. + # When an entity status is set to broken, it is usually associated with a error message. + # + # If it is safe to do so, clients of the model SHOULD just skip broken entities in their processing. + # Clients that do not care about the executability (e.g. metrics) MAY still process the entity or + # perform specific checks to determinate the validity of the entity. + # + # Note that the broken status is not propagated to enclosing and enclosed entities. + # e.g. a broken method does not make the whole module broken. + var is_broken = false is writable end # Something that represents a concern diff --git a/src/modelbuilder_base.nit b/src/modelbuilder_base.nit index 8e1e7c6..4edd46d 100644 --- a/src/modelbuilder_base.nit +++ b/src/modelbuilder_base.nit @@ -186,10 +186,15 @@ class ModelBuilder # Helper function to display an error on a node. # Alias for `self.toolcontext.error(n.hot_location, text)` + # + # This automatically sets `n.is_broken` to true. fun error(n: nullable ANode, text: String) do var l = null - if n != null then l = n.hot_location + if n != null then + l = n.hot_location + n.is_broken = true + end self.toolcontext.error(l, text) end @@ -354,6 +359,22 @@ class ModelBuilder end end +redef class ANode + # The indication that the node did not pass some semantic verifications. + # + # This simple flag is set by a given analysis to say that the node is broken and unusable in + # an execution. + # When a node status is set to broken, it is usually associated with a error message. + # + # If it is safe to do so, clients of the AST SHOULD just skip broken nodes in their processing. + # Clients that do not care about the executability (e.g. metrics) MAY still process the node or + # perform specific checks to determinate the validity of the node. + # + # Note that the broken status is not propagated to parent or children nodes. + # e.g. a broken expression used as argument does not make the whole call broken. + var is_broken = false is writable +end + redef class AType # The mtype associated to the node var mtype: nullable MType = null diff --git a/src/modelize/modelize_class.nit b/src/modelize/modelize_class.nit index 14c8e94..b3ab0bb 100644 --- a/src/modelize/modelize_class.nit +++ b/src/modelize/modelize_class.nit @@ -116,12 +116,15 @@ redef class ModelBuilder #print "new class {mclass}" else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then error(nclassdef, "Error: a class `{name}` is already defined at line {nmodule.mclass2nclassdef[mclass].location.line_start}.") + mclass.is_broken = true return else if nclassdef isa AStdClassdef and nclassdef.n_kwredef == null then error(nclassdef, "Redef Error: `{name}` is an imported class. Add the `redef` keyword to refine it.") + mclass.is_broken = true return else if arity != 0 and mclass.arity != arity then error(nclassdef, "Redef Error: expected {mclass.arity} formal parameter(s) for {mclass.signature_to_s}; got {arity}.") + mclass.is_broken = true return else if nkind != null and mkind != concrete_kind and mclass.kind != mkind then error(nkind, "Redef Error: refinement changed the kind from `{mclass.kind}` to `{mkind}`.") diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index 9cdf578..25502fd 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -110,6 +110,7 @@ redef class ModelBuilder if mpropdef.bound == null then continue if not check_virtual_types_circularity(npropdef, mpropdef.mproperty, mclassdef.bound_mtype, mclassdef.mmodule) then # Invalidate the bound + mpropdef.is_broken = true mpropdef.bound = mclassdef.mmodule.model.null_type end end @@ -373,7 +374,7 @@ redef class ModelBuilder mtype = mtype.undecorate if mtype isa MClassType then vis_type = mtype.mclass.visibility - mmodule_type = mtype.mclass.intro.mmodule + mmodule_type = mtype.mclass.intro_mmodule else if mtype isa MVirtualType then vis_type = mtype.mproperty.visibility mmodule_type = mtype.mproperty.intro_mclassdef.mmodule @@ -720,6 +721,7 @@ redef class ASignature res = false end end + if not res then is_broken = true return res end end @@ -831,8 +833,14 @@ redef class AMethPropdef mprop.is_new = n_kwnew != null if mprop.is_new then mclassdef.mclass.has_new_factory = true if name == "sys" then mprop.is_toplevel = true # special case for sys allowed in `new` factories - self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then + mprop.is_broken = true + return + end else + if mprop.is_broken then + return + end if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) end @@ -841,7 +849,10 @@ redef class AMethPropdef if is_init then for p, n in mclassdef.mprop2npropdef do if p != mprop and p isa MMethod and p.name == name then - check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, p) + if not check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, p) then + mprop.is_broken = true + return + end break end end @@ -993,13 +1004,14 @@ redef class AMethPropdef var mclassdef = mpropdef.mclassdef var mmodule = mclassdef.mmodule var nsig = self.n_signature - var mysignature = self.mpropdef.msignature + var mysignature = mpropdef.msignature if mysignature == null then return # Error thus skiped # Check if nsig != null then if not nsig.check_signature(modelbuilder, mclassdef) then - self.mpropdef.msignature = null # invalidate + mpropdef.msignature = null # invalidate + mpropdef.is_broken = true return # Forward error end end @@ -1014,7 +1026,8 @@ redef class AMethPropdef var ret_type = mysignature.return_mtype if ret_type != null and precursor_ret_type == null then modelbuilder.error(nsig.n_type.as(not null), "Redef Error: `{mpropdef.mproperty}` is a procedure, not a function.") - self.mpropdef.msignature = null + mpropdef.msignature = null + mpropdef.is_broken = true return end @@ -1026,7 +1039,8 @@ redef class AMethPropdef var node = nsig.n_params[i] if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then modelbuilder.error(node, "Redef Error: expected `{prt}` for parameter `{mysignature.mparameters[i].name}'; got `{myt}`.") - self.mpropdef.msignature = null + mpropdef.msignature = null + mpropdef.is_broken = true end end end @@ -1039,7 +1053,8 @@ redef class AMethPropdef ret_type = precursor_ret_type else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then modelbuilder.error(node, "Redef Error: expected `{precursor_ret_type}` for return type; got `{ret_type}`.") - self.mpropdef.msignature = null + mpropdef.msignature = null + mpropdef.is_broken = true end end end diff --git a/src/semantize/scope.nit b/src/semantize/scope.nit index fa10118..c0bb326 100644 --- a/src/semantize/scope.nit +++ b/src/semantize/scope.nit @@ -18,6 +18,7 @@ module scope import phase +import modelbuilder redef class ToolContext # Run `APropdef::do_scope` on each propdef. @@ -191,6 +192,7 @@ private class ScopeVisitor var res = search_label("") if res == null then self.error(nlabel, "Syntax Error: invalid anonymous label.") + node.is_broken = true return null end return res @@ -199,6 +201,7 @@ private class ScopeVisitor var res = search_label(name) if res == null then self.error(nlabel, "Syntax Error: invalid label `{name}`.") + node.is_broken = true return null end return res @@ -218,6 +221,7 @@ private class ScopeVisitor fun error(node: ANode, message: String) do self.toolcontext.error(node.hot_location, message) + node.is_broken = true end end diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 935e1fc..66b47db 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -540,7 +540,7 @@ private class TypeVisitor fun error(node: ANode, message: String) do - self.modelbuilder.toolcontext.error(node.hot_location, message) + self.modelbuilder.error(node, message) end fun get_variable(node: AExpr, variable: Variable): nullable MType @@ -621,6 +621,8 @@ end # A specific method call site with its associated informations. class CallSite + super MEntity + # The associated node for location var node: ANode @@ -659,6 +661,7 @@ class CallSite do var map = v.check_signature(self.node, args, self.mproperty, self.msignature) signaturemap = map + if map == null then is_broken = true return map == null end end @@ -781,6 +784,9 @@ private class PostTypingVisitor redef fun visit(n) do n.visit_all(self) n.accept_post_typing(type_visitor) + if n isa AExpr and n.mtype == null and not n.is_typed then + n.is_broken = true + end end end diff --git a/src/transform.nit b/src/transform.nit index 838b9bc..58541c2 100644 --- a/src/transform.nit +++ b/src/transform.nit @@ -107,7 +107,7 @@ redef class AExpr visit_all(v) - if mtype == null and not is_typed then return # Skip broken + if is_broken then return # Skip broken accept_transform_visitor(v) end @@ -361,6 +361,8 @@ redef class AArrayExpr # ~~~ redef fun full_transform_visitor(v) do + if is_broken then return # Skip broken + var nblock = v.builder.make_block var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)]) diff --git a/tests/sav/base_attr_abstract.res b/tests/sav/base_attr_abstract.res index fa5258d..e9942fa 100644 --- a/tests/sav/base_attr_abstract.res +++ b/tests/sav/base_attr_abstract.res @@ -1,3 +1,2 @@ base_attr_abstract.nit:19,24--31: Error: `abstract` attributes cannot have an initial value. base_attr_abstract.nit:37,12--13: Error: no property `Baz::b=` is inherited. Remove the `redef` keyword to define a new property. -base_attr_abstract.nit:37,15: Error: untyped parameter `x'. diff --git a/tests/sav/base_attr_abstract_alt1.res b/tests/sav/base_attr_abstract_alt1.res index e3a1ef5..3961ab1 100644 --- a/tests/sav/base_attr_abstract_alt1.res +++ b/tests/sav/base_attr_abstract_alt1.res @@ -4,4 +4,3 @@ alt/base_attr_abstract_alt1.nit:21,15--22: Error: `abstract` attributes cannot h alt/base_attr_abstract_alt1.nit:22,15--22: Error: `abstract` attributes cannot have an initial value. alt/base_attr_abstract_alt1.nit:23,15--22: Error: `abstract` attributes cannot have an initial value. alt/base_attr_abstract_alt1.nit:37,12--13: Error: no property `Baz::b=` is inherited. Remove the `redef` keyword to define a new property. -alt/base_attr_abstract_alt1.nit:37,15: Error: untyped parameter `x'. diff --git a/tests/sav/base_attr_abstract_alt2.res b/tests/sav/base_attr_abstract_alt2.res index f79bebd..89698bb 100644 --- a/tests/sav/base_attr_abstract_alt2.res +++ b/tests/sav/base_attr_abstract_alt2.res @@ -1,3 +1,2 @@ alt/base_attr_abstract_alt2.nit:19,24--31: Error: `abstract` attributes cannot have an initial value. alt/base_attr_abstract_alt2.nit:37,12--13: Error: no property `Baz::b=` is inherited. Remove the `redef` keyword to define a new property. -alt/base_attr_abstract_alt2.nit:37,15: Error: untyped parameter `x'. diff --git a/tests/sav/base_attr_abstract_alt3.res b/tests/sav/base_attr_abstract_alt3.res index b112e42..9b4d57b 100644 --- a/tests/sav/base_attr_abstract_alt3.res +++ b/tests/sav/base_attr_abstract_alt3.res @@ -1,3 +1,2 @@ alt/base_attr_abstract_alt3.nit:19,24--31: Error: `abstract` attributes cannot have an initial value. alt/base_attr_abstract_alt3.nit:37,12--13: Error: no property `Baz::b=` is inherited. Remove the `redef` keyword to define a new property. -alt/base_attr_abstract_alt3.nit:37,15: Error: untyped parameter `x'. diff --git a/tests/sav/base_attr_abstract_alt4.res b/tests/sav/base_attr_abstract_alt4.res index 0d349af..776d8cc 100644 --- a/tests/sav/base_attr_abstract_alt4.res +++ b/tests/sav/base_attr_abstract_alt4.res @@ -1,3 +1,2 @@ alt/base_attr_abstract_alt4.nit:19,24--31: Error: `abstract` attributes cannot have an initial value. alt/base_attr_abstract_alt4.nit:37,12--13: Error: no property `Baz::b=` is inherited. Remove the `redef` keyword to define a new property. -alt/base_attr_abstract_alt4.nit:37,15: Error: untyped parameter `x'. diff --git a/tests/sav/error_redef_alt3.res b/tests/sav/error_redef_alt3.res index 4628526..fa8c504 100644 --- a/tests/sav/error_redef_alt3.res +++ b/tests/sav/error_redef_alt3.res @@ -1,2 +1 @@ alt/error_redef_alt3.nit:28,12--13: Error: no property `B::f1` is inherited. Remove the `redef` keyword to define a new property. -alt/error_redef_alt3.nit:28,15: Error: untyped parameter `i'. diff --git a/tests/sav/error_redef_alt6.res b/tests/sav/error_redef_alt6.res index e9a8b09..52016cc 100644 --- a/tests/sav/error_redef_alt6.res +++ b/tests/sav/error_redef_alt6.res @@ -1,2 +1 @@ alt/error_redef_alt6.nit:31,12--13: Error: no property `B::f1` is inherited. Remove the `redef` keyword to define a new property. -alt/error_redef_alt6.nit:31,15: Error: untyped parameter `i'. diff --git a/tests/sav/error_redef_alt9.res b/tests/sav/error_redef_alt9.res index b40a335..3eaa45b 100644 --- a/tests/sav/error_redef_alt9.res +++ b/tests/sav/error_redef_alt9.res @@ -1,2 +1 @@ alt/error_redef_alt9.nit:34,12--13: Error: no property `B::f1` is inherited. Remove the `redef` keyword to define a new property. -alt/error_redef_alt9.nit:34,15: Error: untyped parameter `i'.