logo

class

compiler::Inherit

sys::Obj
  compiler::CompilerSupport
    compiler::CompilerStep
      compiler::Inherit
   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  }