
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.debug("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 }