1 //
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 2 Dec 05 Brian Frank Creation (originally InitShimSlots)
7 // 23 Sep 06 Brian Frank Ported from Java to Fan
8 //
9
10 **
11 ** Inherit processes each TypeDef to resolve the inherited slots.
12 ** This step is used to check invalid inheritances due to conflicting
13 ** slots and invalid overrides.
14 **
15 class Inherit : CompilerStep
16 {
17
18 //////////////////////////////////////////////////////////////////////////
19 // Constructor
20 //////////////////////////////////////////////////////////////////////////
21
22 new make(Compiler compiler)
23 : super(compiler)
24 {
25 }
26
27 //////////////////////////////////////////////////////////////////////////
28 // Run
29 //////////////////////////////////////////////////////////////////////////
30
31 override Void run()
32 {
33 log.verbose("Inherit")
34
35 // at this point OrderByInheritance should have everything
36 // ordered correctly to just do a simple walk
37 walk(types, VisitDepth.typeDef)
38 }
39
40 override Void visitTypeDef(TypeDef t)
41 {
42 // inherit all parent types
43 inheritType(t, t.base)
44 t.mixins.each |CType m| { inheritType(t, m) }
45
46 // check overrides all overrode something
47 t.slotDefs.each |SlotDef slot|
48 {
49 if (slot.isOverride && !slot.overridden && !slot.isAccessor)
50 err("Override of unknown virtual slot '$slot.name'", slot.location)
51 }
52 }
53
54 //////////////////////////////////////////////////////////////////////////
55 // Inherit
56 //////////////////////////////////////////////////////////////////////////
57
58 private Void inheritType(TypeDef t, CType parent)
59 {
60 if (parent == null)
61 {
62 if (t.qname == "sys::Obj") return
63 else throw err("Illegal state", t.location)
64 }
65
66 closure := |CSlot s|
67 {
68 if (parent.isMixin && s.parent.isObj) return
69 try
70 {
71 inheritSlot(t, s)
72 }
73 catch (CompilerErr e)
74 {
75 }
76 }
77
78 // inherit each slot from parent type (if test then
79 // sort the slots to test errors in consistent order)
80 if (compiler.input.isTest)
81 parent.slots.values.sort(|CSlot a, CSlot b->Int| {return a.name <=> b.name}).each(closure)
82 else
83 parent.slots.each(closure)
84 }
85
86 private Void inheritSlot(TypeDef t, CSlot newSlot)
87 {
88 // TODO: I think we need a lot more checking here, especially if
89 // private/internal is public in the Java VM, because right now
90 // we just ignore all ctor, privates, and internals - but they might
91 // cause us real conflicts at JVM/IL emit time if we didn't detect
92 // here. Plus right now overrides of private/internal show up
93 // as unknown virtuals, when in reality we could check them here
94 // as scope errors. So this method needs some refactoring to fully
95 // handle all the cases (along with a comprehensive test)
96
97 // we never inherit constructors, private slots,
98 // or internal slots outside of the pod
99 if (newSlot.isCtor || newSlot.isPrivate ||
100 (newSlot.isInternal && newSlot.parent.pod != t.pod))
101 return
102
103 // check if there is already a slot mapped by that name
104 name := newSlot.name
105 oldSlot := t.slot(name)
106
107 // if a new slot, add it to the type and we are done
108 if (oldSlot == null)
109 {
110 t.addSlot(newSlot)
111 return
112 }
113
114 // if we've inherited the exact same slot from two different
115 // class hiearchies, then no need to continue
116 if (newSlot === oldSlot) return
117
118 // if this is one of the type's slot definitions, then check
119 // that we have a valid inheritance override, in which case
120 // we leave the old slot as the definition for this slot
121 // name - otherwise we will log and throw an error; in all
122 // cases we mark this slot overridden so that we don't report
123 // spurious "Override of unknown virtual slot" errors
124 if (oldSlot.parent === t && !newSlot.isCtor)
125 {
126 slotDef := (SlotDef)oldSlot
127 slotDef.overridden = true
128 checkOverride(t, newSlot, slotDef)
129 return
130 }
131
132 // if the two slots don't have matching signatures
133 // then this is an inheritance conflict
134 if (!matchingSignatures(oldSlot, newSlot))
135 throw err("Inherited slots have conflicting signatures '$oldSlot.qname' and '$newSlot.qname'", t.location)
136
137 // check if there is a clear keeper between old and new slots
138 keep := keep(oldSlot, newSlot)
139 if (keep != null)
140 {
141 if (keep === newSlot) t.replaceSlot(oldSlot, newSlot)
142 return
143 }
144
145 // if both are virtual, then subclass must remove ambiguous
146 if (oldSlot.isVirtual && newSlot.isVirtual)
147 throw err("Must override ambiguous inheritance '$oldSlot.qname' and '$newSlot.qname'", t.location)
148
149 // anything else is an unfixable inheritance conflict
150 throw err("Inheritance conflict '$oldSlot.qname' and '$newSlot.qname'", t.location)
151 }
152
153 **
154 ** Return if two slots have matching signatures
155 **
156 private Bool matchingSignatures(CSlot a, CSlot b)
157 {
158 fa := a as CField
159 fb := b as CField
160 ma := a as CMethod
161 mb := b as CMethod
162
163 if (fa != null && fb != null)
164 return fa.fieldType == fb.fieldType
165
166 if (ma != null && mb != null)
167 return ma.returnType == mb.returnType &&
168 ma.inheritedReturnType == mb.inheritedReturnType &&
169 ma.hasSameParams(mb)
170
171 if (fa != null && mb != null)
172 return fa.fieldType == mb.returnType &&
173 fa.fieldType == mb.inheritedReturnType &&
174 mb.params.size == 0
175
176 if (ma != null && fb != null)
177 return ma.returnType == fb.fieldType &&
178 ma.inheritedReturnType == fb.fieldType &&
179 ma.params.size == 0
180
181 return false
182 }
183
184 **
185 ** Return if there is a clear keeper between a and b - if so
186 ** return the one to keep otherwise return null.
187 **
188 private CSlot keep(CSlot a, CSlot b)
189 {
190 // if one is abstract and one concrete we keep the concrete one
191 if (a.isAbstract && !b.isAbstract) return b
192 if (!a.isAbstract && b.isAbstract) return a
193
194 // keep one if it is a clear override from the other
195 if (a.parent.fits(b.parent)) return a
196 if (b.parent.fits(a.parent)) return b
197
198 return null
199 }
200
201 **
202 ** Check that def is a valid override of the base slot.
203 **
204 private Void checkOverride(TypeDef t, CSlot base, SlotDef def)
205 {
206 loc := def.location
207
208 // check base is virtual
209 if (!base.isVirtual)
210 throw err("Cannot override non-virtual slot '$base.qname'", loc)
211
212 // check override keyword was specified
213 if (!def.isOverride)
214 throw err("Must specify override keyword to override '$base.qname'", loc)
215
216 // check protection scope
217 if (isOverrideProtectionErr(base, def))
218 throw err("Override narrows protection scope of '$base.qname'", loc)
219
220 // check if this is a method/method override
221 if (base is CMethod && def is MethodDef)
222 {
223 checkMethodMethodOverride(t, (CMethod)base, (MethodDef)def)
224 return
225 }
226
227 // check if this is a method/field override
228 if (base is CMethod && def is FieldDef)
229 {
230 checkMethodFieldOverride(t, (CMethod)base, (FieldDef)def)
231 return
232 }
233
234 // check if this is a field/field override
235 if (base is CField && def is FieldDef)
236 {
237 checkFieldFieldOverride(t, (CField)base, (FieldDef)def)
238 return
239 }
240
241 // TODO otherwise this is a potential inheritance conflict
242 throw err("Invalid slot override of '$base.qname'", def.location)
243 }
244
245 private Bool isOverrideProtectionErr(CSlot base, SlotDef def)
246 {
247 if (def.isPublic)
248 return false
249
250 if (def.isProtected)
251 return base.isPublic || base.isInternal
252
253 if (def.isInternal)
254 return base.isPublic || base.isProtected
255
256 return true
257 }
258
259 private Void checkMethodMethodOverride(TypeDef t, CMethod base, MethodDef def)
260 {
261 loc := def.location
262
263 // check if new return type is a subtype of original
264 // return type (we allow covariant return types)
265 defRet := def.returnType
266 baseRet := base.returnType
267 if (!defRet.fits(baseRet) || (defRet.isVoid && !baseRet.isVoid))
268 throw err("Return type mismatch in override of '$base.qname' - '$baseRet' != '$defRet'", loc)
269
270 // if the definition already has a covariant return type, then
271 // it must be exactly the same type as this new override (we
272 // can't have conflicting covariant overrides
273 if (def.inheritedRet != null && def.inheritedRet != base.inheritedReturnType)
274 throw err("Conflicting covariant returns: '$def.inheritedRet' and '$base.inheritedReturnType'", loc)
275
276 // save original return type
277 def.inheritedRet = base.inheritedReturnType
278
279 // check that we have same parameter count
280 if (!base.hasSameParams(def))
281 throw err("Parameter mismatch in override of '$base.qname' - '$base.nameAndParamTypesToStr' != '$def.nameAndParamTypesToStr'", loc)
282
283 // correct override
284 return
285 }
286
287 private Void checkMethodFieldOverride(TypeDef t, CMethod base, FieldDef def)
288 {
289 loc := def.location
290
291 // check that types match (we allow field to be covariant typed)
292 if (!def.fieldType.fits(base.returnType))
293 throw err("Type mismatch in override of '$base.qname' - '$base.returnType' != '$def.fieldType'", loc)
294
295 // save original return type
296 def.inheritedRet = base.inheritedReturnType
297
298 // correct override
299 return
300 }
301
302 private Void checkFieldFieldOverride(TypeDef t, CField base, FieldDef def)
303 {
304 loc := def.location
305
306 // check that types match
307 if (base.fieldType != def.fieldType)
308 throw err("Type mismatch in override of '$base.qname' - '$base.fieldType' != '$def.fieldType'", loc)
309
310 // if overriding a field which has storage, then don't duplicate storage
311 if (!base.isAbstract)
312 def.concreteBase = base
313
314 // correct override
315 return
316 }
317
318
319 }
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 2 Dec 05 Brian Frank Creation (originally InitShimSlots)
7 // 23 Sep 06 Brian Frank Ported from Java to Fan
8 //
9
10 **
11 ** Inherit processes each TypeDef to resolve the inherited slots.
12 ** This step is used to check invalid inheritances due to conflicting
13 ** slots and invalid overrides.
14 **
15 class Inherit : CompilerStep
16 {
17
18 //////////////////////////////////////////////////////////////////////////
19 // Constructor
20 //////////////////////////////////////////////////////////////////////////
21
22 new make(Compiler compiler)
23 : super(compiler)
24 {
25 }
26
27 //////////////////////////////////////////////////////////////////////////
28 // Run
29 //////////////////////////////////////////////////////////////////////////
30
31 override Void run()
32 {
33 log.verbose("Inherit")
34
35 // at this point OrderByInheritance should have everything
36 // ordered correctly to just do a simple walk
37 walk(types, VisitDepth.typeDef)
38 }
39
40 override Void visitTypeDef(TypeDef t)
41 {
42 // inherit all parent types
43 inheritType(t, t.base)
44 t.mixins.each |CType m| { inheritType(t, m) }
45
46 // check overrides all overrode something
47 t.slotDefs.each |SlotDef slot|
48 {
49 if (slot.isOverride && !slot.overridden && !slot.isAccessor)
50 err("Override of unknown virtual slot '$slot.name'", slot.location)
51 }
52 }
53
54 //////////////////////////////////////////////////////////////////////////
55 // Inherit
56 //////////////////////////////////////////////////////////////////////////
57
58 private Void inheritType(TypeDef t, CType parent)
59 {
60 if (parent == null)
61 {
62 if (t.qname == "sys::Obj") return
63 else throw err("Illegal state", t.location)
64 }
65
66 closure := |CSlot s|
67 {
68 if (parent.isMixin && s.parent.isObj) return
69 try
70 {
71 inheritSlot(t, s)
72 }
73 catch (CompilerErr e)
74 {
75 }
76 }
77
78 // inherit each slot from parent type (if test then
79 // sort the slots to test errors in consistent order)
80 if (compiler.input.isTest)
81 parent.slots.values.sort(|CSlot a, CSlot b->Int| {return a.name <=> b.name}).each(closure)
82 else
83 parent.slots.each(closure)
84 }
85
86 private Void inheritSlot(TypeDef t, CSlot newSlot)
87 {
88 // TODO: I think we need a lot more checking here, especially if
89 // private/internal is public in the Java VM, because right now
90 // we just ignore all ctor, privates, and internals - but they might
91 // cause us real conflicts at JVM/IL emit time if we didn't detect
92 // here. Plus right now overrides of private/internal show up
93 // as unknown virtuals, when in reality we could check them here
94 // as scope errors. So this method needs some refactoring to fully
95 // handle all the cases (along with a comprehensive test)
96
97 // we never inherit constructors, private slots,
98 // or internal slots outside of the pod
99 if (newSlot.isCtor || newSlot.isPrivate ||
100 (newSlot.isInternal && newSlot.parent.pod != t.pod))
101 return
102
103 // check if there is already a slot mapped by that name
104 name := newSlot.name
105 oldSlot := t.slot(name)
106
107 // if a new slot, add it to the type and we are done
108 if (oldSlot == null)
109 {
110 t.addSlot(newSlot)
111 return
112 }
113
114 // if we've inherited the exact same slot from two different
115 // class hiearchies, then no need to continue
116 if (newSlot === oldSlot) return
117
118 // if this is one of the type's slot definitions, then check
119 // that we have a valid inheritance override, in which case
120 // we leave the old slot as the definition for this slot
121 // name - otherwise we will log and throw an error; in all
122 // cases we mark this slot overridden so that we don't report
123 // spurious "Override of unknown virtual slot" errors
124 if (oldSlot.parent === t && !newSlot.isCtor)
125 {
126 slotDef := (SlotDef)oldSlot
127 slotDef.overridden = true
128 checkOverride(t, newSlot, slotDef)
129 return
130 }
131
132 // if the two slots don't have matching signatures
133 // then this is an inheritance conflict
134 if (!matchingSignatures(oldSlot, newSlot))
135 throw err("Inherited slots have conflicting signatures '$oldSlot.qname' and '$newSlot.qname'", t.location)
136
137 // check if there is a clear keeper between old and new slots
138 keep := keep(oldSlot, newSlot)
139 if (keep != null)
140 {
141 if (keep === newSlot) t.replaceSlot(oldSlot, newSlot)
142 return
143 }
144
145 // if both are virtual, then subclass must remove ambiguous
146 if (oldSlot.isVirtual && newSlot.isVirtual)
147 throw err("Must override ambiguous inheritance '$oldSlot.qname' and '$newSlot.qname'", t.location)
148
149 // anything else is an unfixable inheritance conflict
150 throw err("Inheritance conflict '$oldSlot.qname' and '$newSlot.qname'", t.location)
151 }
152
153 **
154 ** Return if two slots have matching signatures
155 **
156 private Bool matchingSignatures(CSlot a, CSlot b)
157 {
158 fa := a as CField
159 fb := b as CField
160 ma := a as CMethod
161 mb := b as CMethod
162
163 if (fa != null && fb != null)
164 return fa.fieldType == fb.fieldType
165
166 if (ma != null && mb != null)
167 return ma.returnType == mb.returnType &&
168 ma.inheritedReturnType == mb.inheritedReturnType &&
169 ma.hasSameParams(mb)
170
171 if (fa != null && mb != null)
172 return fa.fieldType == mb.returnType &&
173 fa.fieldType == mb.inheritedReturnType &&
174 mb.params.size == 0
175
176 if (ma != null && fb != null)
177 return ma.returnType == fb.fieldType &&
178 ma.inheritedReturnType == fb.fieldType &&
179 ma.params.size == 0
180
181 return false
182 }
183
184 **
185 ** Return if there is a clear keeper between a and b - if so
186 ** return the one to keep otherwise return null.
187 **
188 private CSlot keep(CSlot a, CSlot b)
189 {
190 // if one is abstract and one concrete we keep the concrete one
191 if (a.isAbstract && !b.isAbstract) return b
192 if (!a.isAbstract && b.isAbstract) return a
193
194 // keep one if it is a clear override from the other
195 if (a.parent.fits(b.parent)) return a
196 if (b.parent.fits(a.parent)) return b
197
198 return null
199 }
200
201 **
202 ** Check that def is a valid override of the base slot.
203 **
204 private Void checkOverride(TypeDef t, CSlot base, SlotDef def)
205 {
206 loc := def.location
207
208 // check base is virtual
209 if (!base.isVirtual)
210 throw err("Cannot override non-virtual slot '$base.qname'", loc)
211
212 // check override keyword was specified
213 if (!def.isOverride)
214 throw err("Must specify override keyword to override '$base.qname'", loc)
215
216 // check protection scope
217 if (isOverrideProtectionErr(base, def))
218 throw err("Override narrows protection scope of '$base.qname'", loc)
219
220 // check if this is a method/method override
221 if (base is CMethod && def is MethodDef)
222 {
223 checkMethodMethodOverride(t, (CMethod)base, (MethodDef)def)
224 return
225 }
226
227 // check if this is a method/field override
228 if (base is CMethod && def is FieldDef)
229 {
230 checkMethodFieldOverride(t, (CMethod)base, (FieldDef)def)
231 return
232 }
233
234 // check if this is a field/field override
235 if (base is CField && def is FieldDef)
236 {
237 checkFieldFieldOverride(t, (CField)base, (FieldDef)def)
238 return
239 }
240
241 // TODO otherwise this is a potential inheritance conflict
242 throw err("Invalid slot override of '$base.qname'", def.location)
243 }
244
245 private Bool isOverrideProtectionErr(CSlot base, SlotDef def)
246 {
247 if (def.isPublic)
248 return false
249
250 if (def.isProtected)
251 return base.isPublic || base.isInternal
252
253 if (def.isInternal)
254 return base.isPublic || base.isProtected
255
256 return true
257 }
258
259 private Void checkMethodMethodOverride(TypeDef t, CMethod base, MethodDef def)
260 {
261 loc := def.location
262
263 // check if new return type is a subtype of original
264 // return type (we allow covariant return types)
265 defRet := def.returnType
266 baseRet := base.returnType
267 if (!defRet.fits(baseRet) || (defRet.isVoid && !baseRet.isVoid))
268 throw err("Return type mismatch in override of '$base.qname' - '$baseRet' != '$defRet'", loc)
269
270 // if the definition already has a covariant return type, then
271 // it must be exactly the same type as this new override (we
272 // can't have conflicting covariant overrides
273 if (def.inheritedRet != null && def.inheritedRet != base.inheritedReturnType)
274 throw err("Conflicting covariant returns: '$def.inheritedRet' and '$base.inheritedReturnType'", loc)
275
276 // save original return type
277 def.inheritedRet = base.inheritedReturnType
278
279 // check that we have same parameter count
280 if (!base.hasSameParams(def))
281 throw err("Parameter mismatch in override of '$base.qname' - '$base.nameAndParamTypesToStr' != '$def.nameAndParamTypesToStr'", loc)
282
283 // correct override
284 return
285 }
286
287 private Void checkMethodFieldOverride(TypeDef t, CMethod base, FieldDef def)
288 {
289 loc := def.location
290
291 // check that types match (we allow field to be covariant typed)
292 if (!def.fieldType.fits(base.returnType))
293 throw err("Type mismatch in override of '$base.qname' - '$base.returnType' != '$def.fieldType'", loc)
294
295 // save original return type
296 def.inheritedRet = base.inheritedReturnType
297
298 // correct override
299 return
300 }
301
302 private Void checkFieldFieldOverride(TypeDef t, CField base, FieldDef def)
303 {
304 loc := def.location
305
306 // check that types match
307 if (base.fieldType != def.fieldType)
308 throw err("Type mismatch in override of '$base.qname' - '$base.fieldType' != '$def.fieldType'", loc)
309
310 // if overriding a field which has storage, then don't duplicate storage
311 if (!base.isAbstract)
312 def.concreteBase = base
313
314 // correct override
315 return
316 }
317
318
319 }