ni_nitdoc: added fast copy past utility to signatures.
[nit.git] / src / modelize_class.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Analysis and verification of class definitions to instantiate model element
18 module modelize_class
19
20 import modelbuilder
21
22 redef class ToolContext
23 var modelize_class_phase: Phase = new ModelizeClassPhase(self, null)
24 end
25
26 private class ModelizeClassPhase
27 super Phase
28
29 redef fun process_nmodule(nmodule)
30 do
31 toolcontext.modelbuilder.build_classes(nmodule)
32 end
33 end
34
35 redef class ModelBuilder
36 # Visit the AST and create the MClass objects
37 private fun build_a_mclass(nmodule: AModule, nclassdef: AClassdef)
38 do
39 var mmodule = nmodule.mmodule.as(not null)
40
41 var name: String
42 var nkind: nullable AClasskind
43 var mkind: MClassKind
44 var nvisibility: nullable AVisibility
45 var mvisibility: nullable MVisibility
46 var arity = 0
47 if nclassdef isa AStdClassdef then
48 name = nclassdef.n_id.text
49 nkind = nclassdef.n_classkind
50 mkind = nkind.mkind
51 nvisibility = nclassdef.n_visibility
52 mvisibility = nvisibility.mvisibility
53 arity = nclassdef.n_formaldefs.length
54 else if nclassdef isa ATopClassdef then
55 name = "Object"
56 nkind = null
57 mkind = interface_kind
58 nvisibility = null
59 mvisibility = public_visibility
60 else if nclassdef isa AMainClassdef then
61 name = "Sys"
62 nkind = null
63 mkind = concrete_kind
64 nvisibility = null
65 mvisibility = public_visibility
66 else
67 abort
68 end
69
70 var mclass = try_get_mclass_by_name(nclassdef, mmodule, name)
71 if mclass == null then
72 mclass = new MClass(mmodule, name, arity, mkind, mvisibility)
73 #print "new class {mclass}"
74 else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then
75 error(nclassdef, "Error: A class {name} is already defined at line {nmodule.mclass2nclassdef[mclass].location.line_start}.")
76 return
77 else if nclassdef isa AStdClassdef and nclassdef.n_kwredef == null then
78 error(nclassdef, "Redef error: {name} is an imported class. Add the redef keyword to refine it.")
79 return
80 else if mclass.arity != arity then
81 error(nclassdef, "Redef error: Formal parameter arity missmatch; got {arity}, expected {mclass.arity}.")
82 return
83 else if nkind != null and mkind != concrete_kind and mclass.kind != mkind then
84 error(nkind, "Error: refinement changed the kind from a {mclass.kind} to a {mkind}")
85 else if nvisibility != null and mvisibility != public_visibility and mclass.visibility != mvisibility then
86 error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}")
87 end
88 nclassdef.mclass = mclass
89 nmodule.mclass2nclassdef[mclass] = nclassdef
90 end
91
92 # Visit the AST and create the MClassDef objects
93 private fun build_a_mclassdef(nmodule: AModule, nclassdef: AClassdef)
94 do
95 var mmodule = nmodule.mmodule.as(not null)
96 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
97 var mclass = nclassdef.mclass
98 if mclass == null then return # Skip error
99 #var mclassdef = nclassdef.mclassdef.as(not null)
100
101 var names = new Array[String]
102 var bounds = new Array[MType]
103 if nclassdef isa AStdClassdef and mclass.arity > 0 then
104 # Collect formal parameter names
105 for i in [0..mclass.arity[ do
106 var nfd = nclassdef.n_formaldefs[i]
107 var ptname = nfd.n_id.text
108 if names.has(ptname) then
109 error(nfd, "Error: A formal parameter type `{ptname}' already exists")
110 return
111 end
112 names.add(ptname)
113 end
114
115 # Revolve bound for formal parameter names
116 for i in [0..mclass.arity[ do
117 var nfd = nclassdef.n_formaldefs[i]
118 var nfdt = nfd.n_type
119 if nfdt != null then
120 var bound = resolve_mtype_unchecked(nclassdef, nfdt, false)
121 if bound == null then return # Forward error
122 if bound.need_anchor then
123 # No F-bounds!
124 error(nfd, "Error: Formal parameter type `{names[i]}' bounded with a formal parameter type")
125 else
126 bounds.add(bound)
127 end
128 else if mclass.mclassdefs.is_empty then
129 # No bound, then implicitely bound by nullable Object
130 bounds.add(objectclass.mclass_type.as_nullable)
131 else
132 # Inherit the bound
133 bounds.add(mclass.intro.bound_mtype.arguments[i])
134 end
135 end
136 end
137
138 var bound_mtype = mclass.get_mtype(bounds)
139 var mclassdef = new MClassDef(mmodule, bound_mtype, nclassdef.location, names)
140 nclassdef.mclassdef = mclassdef
141 self.mclassdef2nclassdef[mclassdef] = nclassdef
142
143 if mclassdef.is_intro then
144 self.toolcontext.info("{mclassdef} introduces new {mclass.kind} {mclass.full_name}", 3)
145 else
146 self.toolcontext.info("{mclassdef} refine {mclass.kind} {mclass.full_name}", 3)
147 end
148 end
149
150 # Visit the AST and set the super-types of the MClassdef objects
151 private fun collect_a_mclassdef_inheritance(nmodule: AModule, nclassdef: AClassdef)
152 do
153 var mmodule = nmodule.mmodule.as(not null)
154 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
155 var mclass = nclassdef.mclass.as(not null)
156 var mclassdef = nclassdef.mclassdef.as(not null)
157
158 var specobject = true
159 var supertypes = new Array[MClassType]
160 if nclassdef isa AStdClassdef then
161 for nsc in nclassdef.n_superclasses do
162 specobject = false
163 var ntype = nsc.n_type
164 var mtype = resolve_mtype_unchecked(nclassdef, ntype, false)
165 if mtype == null then continue # Skip because of error
166 if not mtype isa MClassType then
167 error(ntype, "Error: supertypes cannot be a formal type")
168 return
169 end
170 supertypes.add mtype
171 #print "new super : {mclass} < {mtype}"
172 end
173 end
174 if specobject and mclass.name != "Object" and objectclass != null and mclassdef.is_intro then
175 supertypes.add objectclass.mclass_type
176 end
177
178 mclassdef.set_supertypes(supertypes)
179 if not supertypes.is_empty then self.toolcontext.info("{mclassdef} new super-types: {supertypes.join(", ")}", 3)
180 end
181
182 # Check the validity of the specialization heirarchy
183 private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef)
184 do
185 var mmodule = nmodule.mmodule.as(not null)
186 var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
187 var mclass = nclassdef.mclass.as(not null)
188 var mclassdef = nclassdef.mclassdef.as(not null)
189
190 for s in mclassdef.supertypes do
191 if s.is_subtype(mmodule, mclassdef.bound_mtype, mclassdef.bound_mtype) then
192 error(nclassdef, "Error: Inheritance loop for class {mclass} with type {s}")
193 end
194 end
195 end
196
197 # Build the classes of the module `nmodule'.
198 # REQUIRE: classes of imported modules are already build. (let `phase' do the job)
199 private fun build_classes(nmodule: AModule)
200 do
201 # Force building recursively
202 if nmodule.build_classes_is_done then return
203 nmodule.build_classes_is_done = true
204 var mmodule = nmodule.mmodule.as(not null)
205 for imp in mmodule.in_importation.direct_greaters do
206
207 build_classes(mmodule2nmodule[imp])
208 end
209
210 # Create all classes
211 for nclassdef in nmodule.n_classdefs do
212 self.build_a_mclass(nmodule, nclassdef)
213 end
214
215 # Create all classdefs
216 for nclassdef in nmodule.n_classdefs do
217 self.build_a_mclassdef(nmodule, nclassdef)
218 end
219
220 for nclassdef in nmodule.n_classdefs do
221 if nclassdef.mclassdef == null then return # forward error
222 end
223
224 # Create inheritance on all classdefs
225 for nclassdef in nmodule.n_classdefs do
226 self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
227 end
228
229 # Create the mclassdef hierarchy
230 for nclassdef in nmodule.n_classdefs do
231 var mclassdef = nclassdef.mclassdef.as(not null)
232 mclassdef.add_in_hierarchy
233 end
234
235 # Check inheritance
236 for nclassdef in nmodule.n_classdefs do
237 self.check_supertypes(nmodule, nclassdef)
238 end
239
240 # Check unchecked ntypes
241 for nclassdef in nmodule.n_classdefs do
242 if nclassdef isa AStdClassdef then
243 # check bound of formal parameter
244 for nfd in nclassdef.n_formaldefs do
245 var nfdt = nfd.n_type
246 if nfdt != null and nfdt.mtype != null then
247 var bound = resolve_mtype(nclassdef, nfdt)
248 if bound == null then return # Forward error
249 end
250 end
251 # check declared super types
252 for nsc in nclassdef.n_superclasses do
253 var ntype = nsc.n_type
254 if ntype.mtype != null then
255 var mtype = resolve_mtype(nclassdef, ntype)
256 if mtype == null then return # Forward error
257 end
258 end
259 end
260
261 end
262
263 # TODO: Check that the super-class is not intrusive
264
265 # TODO: Check that the super-class is not already known (by transitivity)
266 end
267
268 # Register the nclassdef associated to each mclassdef
269 # FIXME: why not refine the MClassDef class with a nullable attribute?
270 var mclassdef2nclassdef: HashMap[MClassDef, AClassdef] = new HashMap[MClassDef, AClassdef]
271
272 # Return the static type associated to the node `ntype'.
273 # `classdef' is the context where the call is made (used to understand formal types)
274 # The mmodule used as context is `nclassdef.mmodule'
275 # In case of problem, an error is displayed on `ntype' and null is returned.
276 # FIXME: the name "resolve_mtype" is awful
277 fun resolve_mtype_unchecked(nclassdef: AClassdef, ntype: AType, with_virtual: Bool): nullable MType
278 do
279 var name = ntype.n_id.text
280 var mclassdef = nclassdef.mclassdef
281 var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
282 var res: MType
283
284 # Check virtual type
285 if mclassdef != null and with_virtual then
286 var prop = try_get_mproperty_by_name(ntype, mclassdef, name).as(nullable MVirtualTypeProp)
287 if prop != null then
288 if not ntype.n_types.is_empty then
289 error(ntype, "Type error: formal type {name} cannot have formal parameters.")
290 end
291 res = prop.mvirtualtype
292 if ntype.n_kwnullable != null then res = res.as_nullable
293 ntype.mtype = res
294 return res
295 end
296 end
297
298 # Check parameter type
299 if mclassdef != null and mclassdef.parameter_names.has(name) then
300 if not ntype.n_types.is_empty then
301 error(ntype, "Type error: formal type {name} cannot have formal parameters.")
302 end
303 for i in [0..mclassdef.parameter_names.length[ do
304 if mclassdef.parameter_names[i] == name then
305 res = mclassdef.mclass.mclass_type.arguments[i]
306 if ntype.n_kwnullable != null then res = res.as_nullable
307 ntype.mtype = res
308 return res
309 end
310 end
311 abort
312 end
313
314 # Check class
315 var mclass = try_get_mclass_by_name(ntype, mmodule, name)
316 if mclass != null then
317 var arity = ntype.n_types.length
318 if arity != mclass.arity then
319 if arity == 0 then
320 error(ntype, "Type error: '{name}' is a generic class.")
321 else if mclass.arity == 0 then
322 error(ntype, "Type error: '{name}' is not a generic class.")
323 else
324 error(ntype, "Type error: '{name}' has {mclass.arity} parameters ({arity} are provided).")
325 end
326 return null
327 end
328 if arity == 0 then
329 res = mclass.mclass_type
330 if ntype.n_kwnullable != null then res = res.as_nullable
331 ntype.mtype = res
332 return res
333 else
334 var mtypes = new Array[MType]
335 for nt in ntype.n_types do
336 var mt = resolve_mtype_unchecked(nclassdef, nt, with_virtual)
337 if mt == null then return null # Forward error
338 mtypes.add(mt)
339 end
340 res = mclass.get_mtype(mtypes)
341 if ntype.n_kwnullable != null then res = res.as_nullable
342 ntype.mtype = res
343 return res
344 end
345 end
346
347 # If everything fail, then give up :(
348 error(ntype, "Type error: class {name} not found in module {mmodule}.")
349 return null
350 end
351
352 # Return the static type associated to the node `ntype'.
353 # `classdef' is the context where the call is made (used to understand formal types)
354 # The mmodule used as context is `nclassdef.mmodule'
355 # In case of problem, an error is displayed on `ntype' and null is returned.
356 # FIXME: the name "resolve_mtype" is awful
357 fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
358 do
359 var mtype = ntype.mtype
360 if mtype == null then mtype = resolve_mtype_unchecked(nclassdef, ntype, true)
361 if mtype == null then return null # Forward error
362
363 if ntype.checked_mtype then return mtype
364 if mtype isa MGenericType then
365 var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
366 var mclassdef = nclassdef.mclassdef
367 var mclass = mtype.mclass
368 for i in [0..mclass.arity[ do
369 var bound = mclass.intro.bound_mtype.arguments[i]
370 var nt = ntype.n_types[i]
371 var mt = resolve_mtype(nclassdef, nt)
372 if mt == null then return null # forward error
373 if not mt.is_subtype(mmodule, mclassdef.bound_mtype, bound) then
374 error(nt, "Type error: expected {bound}, got {mt}")
375 return null
376 end
377 end
378 end
379 ntype.checked_mtype = true
380 return mtype
381 end
382
383 end
384
385 redef class AModule
386 # Flag that indicate if the class building is already completed
387 var build_classes_is_done: Bool = false
388 # What is the AClassdef associated to a MClass?
389 # Used to check multiple definition of a class.
390 var mclass2nclassdef: Map[MClass, AClassdef] = new HashMap[MClass, AClassdef]
391 end
392
393 redef class AClassdef
394 # The associated MClass once build by a `ModelBuilder'
395 var mclass: nullable MClass
396 # The associated MClassDef once build by a `ModelBuilder'
397 var mclassdef: nullable MClassDef
398 end
399
400 redef class AClasskind
401 # The class kind associated with the AST node class
402 private fun mkind: MClassKind is abstract
403 end
404 redef class AConcreteClasskind
405 redef fun mkind do return concrete_kind
406 end
407 redef class AAbstractClasskind
408 redef fun mkind do return abstract_kind
409 end
410 redef class AInterfaceClasskind
411 redef fun mkind do return interface_kind
412 end
413 redef class AEnumClasskind
414 redef fun mkind do return enum_kind
415 end
416 redef class AExternClasskind
417 redef fun mkind do return extern_kind
418 end
419
420 redef class AType
421 # The mtype associated to the node
422 var mtype: nullable MType = null
423
424 # Is the mtype a valid one?
425 var checked_mtype: Bool = false
426 end