phase: add `semantize_is_lazy` flag to enable the separate analysis of npropdefs
[nit.git] / src / phase.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Phases of the processing of nit programs
16 module phase
17
18 import toolcontext
19 import parser
20 import poset
21
22 redef class ToolContext
23 # The various registered phases to performs
24 # The order in the poset is the dependance of phases
25 #
26 # While you can directly modify the poset (nodes and edges),
27 # it is often simpler to use the constructor in `Phase`
28 var phases = new POSet[Phase]
29
30 # --disable-phase
31 var opt_disable_phase = new OptionArray("DEBUG: Disable a specific phase; use `list` to get the list.", "--disable-phase")
32
33 redef init
34 do
35 super
36
37 option_context.add_option(opt_disable_phase)
38 end
39
40 redef fun process_options(args)
41 do
42 super
43
44 for v in opt_disable_phase.value do
45 if v == "list" then
46 for p in phases_list do
47 var deps = p.in_hierarchy.direct_greaters
48 if deps.is_empty then
49 print p
50 else
51 print "{p} (dep: {deps.join(", ")})"
52 end
53 end
54 exit 0
55 end
56
57 var found = false
58 for p in phases do
59 if v != p.to_s then continue
60 found = true
61 p.disabled = true
62 end
63 if not found then fatal_error(null, "Error: no phase named `{v}`. Use `list` to list all phases.")
64 end
65 end
66
67 fun phases_list: Sequence[Phase]
68 do
69 var phases = self.phases.to_a
70 self.phases.sort(phases)
71 return phases
72 end
73
74 # Is `phase_process_npropdef` not called automatically by `run_phases`?
75 #
76 # When set to true, it is the responsibility of the tools
77 #
78 # Is false by default.
79 var semantize_is_lazy = false is writable
80
81 # Set of already analyzed modules.
82 private var phased_modules = new HashSet[AModule]
83
84 # Run all registered phases on a set of modules
85 fun run_phases(nmodules: Collection[AModule])
86 do
87 var time0 = get_time
88 self.info("*** SEMANTIC ANALYSIS ***", 1)
89 #phases.show_dot
90
91 var phases = phases_list
92
93 for phase in phases do
94 self.info(" registered phases: {phase}", 2)
95 end
96
97 for nmodule in nmodules do
98 if phased_modules.has(nmodule) then continue
99 phased_modules.add nmodule
100
101 self.info("Semantic analysis module {nmodule.location.file.filename}", 2)
102
103 var vannot = new AnnotationPhaseVisitor
104 vannot.enter_visit(nmodule)
105
106 for phase in phases do
107 if phase.disabled then continue
108 self.info(" phase: {phase}", 3)
109 assert phase.toolcontext == self
110 var errcount = self.error_count
111 phase.process_nmodule(nmodule)
112 if errcount != self.error_count then
113 self.check_errors
114 break
115 end
116 errcount = self.error_count
117 for nclassdef in nmodule.n_classdefs do
118 assert phase.toolcontext == self
119 phase.process_nclassdef(nclassdef)
120 if not semantize_is_lazy then for npropdef in nclassdef.n_propdefs do
121 assert phase.toolcontext == self
122 phase_process_npropdef(phase, npropdef)
123 end
124 end
125 if errcount != self.error_count then
126 self.check_errors
127 break
128 end
129 for na in vannot.annotations do
130 phase.process_annotated_node(na.parent.parent.as(not null), na)
131 end
132 if errcount != self.error_count then
133 self.check_errors
134 break
135 end
136 end
137 self.check_errors
138 end
139
140 var time1 = get_time
141 self.info("*** END SEMANTIC ANALYSIS: {time1-time0} ***", 2)
142
143 errors_info
144 end
145
146 fun phase_process_npropdef(phase: Phase, npropdef: APropdef)
147 do
148 phase.process_npropdef(npropdef)
149 end
150
151 # Run the phase on the given npropdef.
152 # Does nothing if `semantize_is_lazy` is false.
153 fun run_phases_on_npropdef(npropdef: APropdef)
154 do
155 if not semantize_is_lazy then return
156 if npropdef.is_phased then return
157 npropdef.is_phased = true
158
159 #self.info("Semantic analysis of property {npropdef.location.file.filename}", 0)
160
161 var phases = phases_list
162 for phase in phases do
163 if phase.disabled then continue
164 assert phase.toolcontext == self
165 phase_process_npropdef(phase, npropdef)
166 self.check_errors
167 end
168 end
169 end
170
171 redef class APropdef
172 # Is the propdef already analyzed by `run_phases_on_npropdef`.
173 # Unused unless `semantize_is_lazy` is true.
174 private var is_phased = false
175 end
176
177 # Collect all annotation
178 private class AnnotationPhaseVisitor
179 super Visitor
180
181 # The collected annotations
182 var annotations = new Array[AAnnotation]
183
184 redef fun visit(n)
185 do
186 n.visit_all(self)
187 if n isa AAnnotation then annotations.add n
188 end
189 end
190
191 # Abstraction of steps in the analysis/processing of Nit programs
192 # Specific phases should implements one of the `process_*` method
193 abstract class Phase
194 # The toolcontext instance attached to the phase
195 var toolcontext: ToolContext
196
197 # The dependence relation of the phase with the other phases
198 var in_hierarchy: POSetElement[Phase] is noinit
199
200 # The explicit dependences, used to initialize `in_importation`
201 var depends: nullable Collection[Phase]
202
203 # Initialize and register a phase to the toolcontext
204 init
205 do
206 in_hierarchy = toolcontext.phases.add_node(self)
207 var depends = self.depends
208 if depends != null then
209 for d in depends do
210 toolcontext.phases.add_edge(self, d)
211 end
212 end
213 end
214
215 # By default, the name is the lowercased prefix of the classname
216 redef fun to_s do return class_name.strip_extension("Phase").to_lower
217
218 # Is the phase globally disabled?
219 # A disabled phase is not called automatically called by `ToolContext::run_phases` and cie.
220 #
221 # Warning: disabling a phase may cause subsequent phases to work in a degraded way or to fail.
222 var disabled = false is writable
223
224 # Specific actions to execute on the whole tree of a module
225 # @toimplement
226 fun process_nmodule(nmodule: AModule) do end
227
228 # Specific actions to execute on the tree of a class definition
229 # Note that the order of the visit is the one of the file
230 # @toimplement
231 fun process_nclassdef(nclassdef: AClassdef) do end
232
233 # Specific actions to execute on the tree of a property
234 # Note that the order of the visit is the one of the file
235 # @toimplement
236 fun process_npropdef(npropdef: APropdef) do end
237
238 # Specific actions to execute on annotated nodes
239 # Note that the order of the visit is the one of the file
240 # @toimplement
241 fun process_annotated_node(node: ANode, nat: AAnnotation) do end
242 end