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