auto_super_init: use CallSite
[nit.git] / src / auto_super_init.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 # Computing of super-constructors that must be implicitely called at the begin of constructors.
18 # The current rules are a bit crazy but whatever.
19 module auto_super_init
20
21 import typing
22 import modelbuilder
23 import phase
24
25 redef class ToolContext
26 var auto_super_init_phase: Phase = new AutoSuperInitPhase(self, [typing_phase])
27 end
28
29 private class AutoSuperInitPhase
30 super Phase
31 redef fun process_npropdef(npropdef) do if npropdef isa AConcreteMethPropdef then npropdef.do_auto_super_init(toolcontext.modelbuilder)
32 end
33
34 private class AutoSuperInitVisitor
35 super Visitor
36 init
37 do
38 end
39
40 redef fun visit(n)
41 do
42 n.accept_auto_super_init(self)
43 n.visit_all(self)
44 end
45
46 var has_explicit_super_init: Bool = false
47 end
48
49
50 redef class AConcreteMethPropdef
51 # In case of constructor, the list of implicit auto super init constructors invoked (if needed)
52 var auto_super_inits: nullable Array[CallSite] = null
53
54 fun do_auto_super_init(modelbuilder: ModelBuilder)
55 do
56 var mclassdef = self.parent.as(AClassdef).mclassdef.as(not null)
57 var mpropdef = self.mpropdef.as(not null)
58 var mmodule = mpropdef.mclassdef.mmodule
59 var anchor = mclassdef.bound_mtype
60 var recvtype = mclassdef.mclass.mclass_type
61
62 # Collect only for constructors
63 if not mpropdef.mproperty.is_init then return
64
65 # FIXME: THIS IS STUPID (be here to keep the old code working)
66 if not mpropdef.mclassdef.is_intro then return
67
68 # Do we inherit for a constructor?
69 var skip = true
70 for cd in mclassdef.in_hierarchy.direct_greaters do
71 if cd.mclass.kind.need_init then skip = false
72 end
73 if skip then return
74
75 # Now we search for the absence of any explicit super-init invocation
76 # * via a "super"
77 # * via a call of an other init
78 var nblock = self.n_block
79 if nblock != null then
80 var v = new AutoSuperInitVisitor
81 v.enter_visit(nblock)
82 if v.has_explicit_super_init then return
83 end
84
85 # Still here? So it means that we must determine what super inits need to be automatically invoked
86
87 var auto_super_inits = new Array[CallSite]
88 for msupertype in mclassdef.supertypes do
89 # FIXME: the order is quite arbitrary
90 if not msupertype.mclass.kind.need_init then continue
91 msupertype = msupertype.anchor_to(mmodule, mclassdef.bound_mtype)
92 var candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, mpropdef.mproperty.name)
93 if candidate == null then
94 candidate = modelbuilder.try_get_mproperty_by_name2(self, mmodule, msupertype, "init")
95 end
96 if candidate == null then
97 modelbuilder.error(self, "Error: Cannot do an implicit constructor call in {mpropdef}; there is no constructor named {mpropdef.mproperty.name} in {msupertype}.")
98 return
99 end
100 assert candidate isa MMethod
101
102 var candidatedefs = candidate.lookup_definitions(mmodule, anchor)
103 var candidatedef = candidatedefs.first
104 # TODO, we drop the others propdefs in the callsite, that is not great :(
105
106 var msignature = candidatedef.msignature
107 msignature = msignature.resolve_for(recvtype, anchor, mmodule, true)
108
109 var callsite = new CallSite(self, recvtype, true, candidate, candidatedef, msignature, false)
110 auto_super_inits.add(callsite)
111 end
112 if auto_super_inits.is_empty then
113 modelbuilder.error(self, "Error: No constructors to call implicitely in {mpropdef}. Call one explicitely.")
114 return
115 end
116 for auto_super_init in auto_super_inits do
117 var auto_super_init_def = auto_super_init.mpropdef
118 var msig = mpropdef.msignature.as(not null)
119 var supermsig = auto_super_init.msignature
120 if supermsig.arity > msig.arity then
121 modelbuilder.error(self, "Error: Cannot do an implicit constructor call to {auto_super_init_def}{supermsig}. Expected at least {supermsig.arity} arguments, got {msig.arity}.")
122 continue
123 end
124 var i = 0
125 for sp in supermsig.mparameters do
126 var p = msig.mparameters[i]
127 var sub = p.mtype
128 var sup = sp.mtype
129 if not sub.is_subtype(mmodule, anchor, sup) then
130 modelbuilder.error(self, "Error: Cannot do an implicit constructor call to {auto_super_init_def}{supermsig}. Expected argument #{i} of type {sp.mtype}, got implicit argument {p.name} of type {p.mtype}.")
131 break
132 end
133 i += 1
134 end
135 end
136 self.auto_super_inits = auto_super_inits
137 end
138
139 end
140
141 redef class ANode
142 private fun accept_auto_super_init(v: AutoSuperInitVisitor) do end
143 end
144
145
146 redef class ASendExpr
147 redef fun accept_auto_super_init(v)
148 do
149 var mproperty = self.callsite.mproperty
150 if mproperty == null then return
151 if mproperty.is_init then
152 v.has_explicit_super_init = true
153 end
154 end
155 end
156
157 redef class ASuperExpr
158 redef fun accept_auto_super_init(v)
159 do
160 # If the super is a standard call-next-method then there it is considered am explicit super init call
161 # The the super is a "super int" then it is also an explicit super init call
162 v.has_explicit_super_init = true
163 end
164 end