logo
class

compiler::CheckInheritance

sys::Obj
  compiler::CompilerSupport
    compiler::CompilerStep
      compiler::CheckInheritance
   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  ** CheckInheritance is used to check invalid extends or mixins.
  12  **
  13  class CheckInheritance : CompilerStep
  14  {
  15  
  16  //////////////////////////////////////////////////////////////////////////
  17  // Constructor
  18  //////////////////////////////////////////////////////////////////////////
  19  
  20    new make(Compiler compiler)
  21      : super(compiler)
  22    {
  23    }
  24  
  25  //////////////////////////////////////////////////////////////////////////
  26  // Run
  27  //////////////////////////////////////////////////////////////////////////
  28  
  29    override Void run()
  30    {
  31      log.verbose("CheckInheritance")
  32      walk(types, VisitDepth.typeDef)
  33      bombIfErr
  34    }
  35  
  36    override Void visitTypeDef(TypeDef t)
  37    {
  38      // check out of order base vs mixins first
  39      if (!checkOutOfOrder(t)) return
  40  
  41      // check extends
  42      checkExtends(t, t.base)
  43  
  44      // check each mixin
  45      t.mixins.each |CType m| { checkMixin(t, m) }
  46    }
  47  
  48  //////////////////////////////////////////////////////////////////////////
  49  // Checks
  50  //////////////////////////////////////////////////////////////////////////
  51  
  52    private Bool checkOutOfOrder(TypeDef t)
  53    {
  54      if (!t.baseSpecified)
  55      {
  56        cls := t.mixins.find |CType x->Bool| { return !x.isMixin }
  57        if (cls != null)
  58        {
  59          err("Invalid inheritance order, ensure class '$cls' comes first before mixins", t.location)
  60          return false
  61        }
  62      }
  63      return true
  64    }
  65  
  66    private Void checkExtends(TypeDef t, CType base)
  67    {
  68      // base is null only for sys::Obj
  69      if (base == null && t.qname == "sys::Obj")
  70        return
  71  
  72      // ensure mixin doesn't extend class
  73      if (t.isMixin && t.baseSpecified)
  74        err("Mixin '$t.name' cannot extend class '$base'", t.location)
  75  
  76      // ensure enum doesn't extend class
  77      if (t.isEnum && t.baseSpecified)
  78        err("Enum '$t.name' cannot extend class '$base'", t.location)
  79  
  80      // check extends a mixin
  81      if (base.isMixin)
  82        err("Class '$t.name' cannot extend mixin '$base'", t.location)
  83  
  84      // check extends parameterized type
  85      if (base.isParameterized)
  86        err("Class '$t.name' cannot extend parameterized type '$base'", t.location)
  87  
  88      // check extends final
  89      if (base.isFinal)
  90        err("Class '$t.name' cannot extend final class '$base'", t.location)
  91  
  92      // check extends internal scoped outside my pod
  93      if (base.isInternal && t.pod != base.pod)
  94        err("Class '$t.name' cannot access internal scoped class '$base'", t.location)
  95    }
  96  
  97    private Void checkMixin(TypeDef t, CType m)
  98    {
  99      // check mixins a class
 100      if (!m.isMixin)
 101      {
 102        if (t.isMixin)
 103          err("Mixin '$t.name' cannot extend class '$m'", t.location)
 104        else
 105          err("Class '$t.name' cannot mixin class '$m'", t.location)
 106      }
 107  
 108      // check extends internal scoped outside my pod
 109      if (m.isInternal && t.pod != m.pod)
 110        err("Type '$t.name' cannot access internal scoped mixin '$m'", t.location)
 111    }
 112  
 113  }