Joos1W Compiler Framework
All Classes Functions Typedefs Pages
HierarchyChecker.cc
1 #include "semantic/HierarchyChecker.h"
2 
3 #include <set>
4 #include <utility>
5 #include "ast/AstNode.h"
6 #include "ast/DeclContext.h"
7 #include "utils/Utils.h"
8 
9 namespace semantic {
10 
11 static bool isSameMethodSignature(ast::MethodDecl const* method1,
12  ast::MethodDecl const* method2) {
13  if(method1->name() != method2->name()) return false;
14  if(method1->parameters().size() != method2->parameters().size()) return false;
15 
16  for(size_t i = 0; i < method1->parameters().size(); i++) {
17  if(*method1->parameters()[i]->type() != *method2->parameters()[i]->type()) {
18  return false;
19  }
20  }
21 
22  return true;
23 }
24 
25 bool HierarchyChecker::isSuperClass(ast::ClassDecl const* super,
26  ast::ClassDecl const* sub) {
27  if(super == sub) return true;
28  for(auto superClass : inheritanceMap_[sub]) {
29  if(auto directSuper = dyn_cast<ast::ClassDecl>(superClass)) {
30  if(isSuperClass(super, directSuper)) return true;
31  }
32  }
33  return false;
34 }
35 
36 bool HierarchyChecker::isSuperInterface(ast::InterfaceDecl const* super,
37  ast::Decl const* sub) {
38  if(super == sub) return true;
39  for(auto superInterface : inheritanceMap_[sub]) {
40  if(isSuperInterface(super, superInterface)) return true;
41  }
42  return false;
43 }
44 
45 void HierarchyChecker::setInheritedMembersHelper(ast::Decl const* node,
46  ast::Decl const* parent) {
47  for(auto member : getInheritedMembers(parent)) {
48  bool isHidden = false;
49  for(auto memberInherited : memberInheritancesMap_[node]) {
50  if(memberInherited->name() == member->name()) {
51  isHidden = true;
52  }
53  }
54  if(!isHidden) memberInheritancesMap_[node].insert(member);
55  }
56 }
57 
58 void HierarchyChecker::checkMethodInheritanceHelper(
59  ast::Decl const* node, std::pmr::set<ast::Decl const*>& visited) {
60  // Mark the node as visited
61  visited.insert(node);
62  std::pmr::vector<ast::MethodDecl const*> inheritedMethods;
63  memberInheritancesMap_[node] = std::pmr::set<ast::TypedDecl const*>{};
64  auto nodeAsClass = dyn_cast<ast::ClassDecl>(node);
65  if(nodeAsClass) {
66  for(auto member : nodeAsClass->fields()) {
67  memberInheritancesMap_[node].insert(member);
68  }
69  }
70 
71  for(auto super : inheritanceMap_[node]) {
72  if(auto superClass = dyn_cast<ast::ClassDecl>(super)) {
73  if(!visited.count(superClass)) {
74  checkMethodInheritanceHelper(superClass, visited);
75  } else if(!isInheritedSet(superClass)) {
76  diag.ReportError(superClass->location())
77  << "Cycle is detected in the inheritance graph. "
78  << superClass->name();
79  continue;
80  }
81  for(auto method : getInheritedMethods(superClass)) {
82  inheritedMethods.emplace_back(method);
83  }
84  setInheritedMembersHelper(node, superClass);
85  } else if(auto superInterface = dyn_cast<ast::InterfaceDecl>(super)) {
86  if(!visited.count(superInterface)) {
87  checkMethodInheritanceHelper(superInterface, visited);
88  } else if(!isInheritedSet(superInterface)) {
89  diag.ReportError(superInterface->location())
90  << "Cycle is detected in the inheritance graph. "
91  << superInterface->name();
92  continue;
93  }
94  for(auto method : getInheritedMethods(superInterface)) {
95  inheritedMethods.emplace_back(method);
96  }
97  } else if(super != nullptr) {
98  std::unreachable();
99  }
100  }
101  if(auto classDecl = dyn_cast<ast::ClassDecl>(node)) {
102  checkClassMethod(classDecl, inheritedMethods);
103  checkClassConstructors(classDecl);
104  if(diag.Verbose(2)) {
105  diag.ReportDebug(2) << "Class: " << classDecl->name() << std::endl;
106  diag.ReportDebug(2) << "Inherited fields: " << std::endl;
107  for(auto member : memberInheritancesMap_[node]) {
108  diag.ReportDebug(2) << "\t" << member->name() << std::endl;
109  }
110  }
111  } else if(auto interfaceDecl = dyn_cast<ast::InterfaceDecl>(node)) {
112  checkInterfaceMethod(interfaceDecl, inheritedMethods);
113  }
114 }
115 
116 void HierarchyChecker::checkMethodInheritance() {
117  std::pmr::set<ast::Decl const*> visited;
118  for(auto cu : lu_->compliationUnits()) {
119  auto body = cu->body();
120  // if the body is null, continue to the next iteration
121  if(!body) continue;
122  if(auto classDecl = dyn_cast<ast::ClassDecl>(body)) {
123  if(visited.count(classDecl)) continue;
124  checkMethodInheritanceHelper(classDecl, visited);
125  } else if(auto interfaceDecl = dyn_cast<ast::InterfaceDecl>(body)) {
126  if(visited.count(interfaceDecl)) continue;
127  checkMethodInheritanceHelper(interfaceDecl, visited);
128  }
129  }
130 }
131 
132 void HierarchyChecker::checkInheritance() {
133  inheritanceMap_.clear();
134  for(auto cu : lu_->compliationUnits()) {
135  auto body = cu->body();
136  // if the body is null, continue to the next iteration
137  if(!body) continue;
138 
139  if(auto classDecl = dyn_cast<ast::ClassDecl>(body)) {
140  // if there is a superclass
141  if(auto superClass = classDecl->superClasses()[0]) {
142  auto superClassDecl = dyn_cast<ast::ClassDecl>(superClass->decl());
143  // class cannot extend an interface
144  if(!superClassDecl) {
145  diag.ReportError(classDecl->location())
146  << "A class must not extend an interface. "
147  << classDecl->name();
148  continue;
149  }
150  // class cannot extend a final class
151  if(superClassDecl->modifiers().isFinal()) {
152  diag.ReportError(classDecl->location())
153  << "A class must not extend a final class"
154  << classDecl->name();
155  }
156  inheritanceMap_[classDecl].insert(superClassDecl);
157  } else if(auto objectClass = classDecl->superClasses()[1]) {
158  // if the class does not extend any class, it extends the object class
159  inheritanceMap_[classDecl].insert(
160  cast<ast::ClassDecl>(objectClass->decl()));
161  }
162 
163  // check if interfaces are valid
164  for(auto interface : classDecl->interfaces()) {
165  // check if there are duplicate interfaces
166  for(auto other : classDecl->interfaces()) {
167  if(interface == other) continue;
168  if(interface->decl() == other->decl()) {
169  diag.ReportError(classDecl->location())
170  << "A class must not implement the same interface twice. "
171  << classDecl->name();
172  }
173  }
174 
175  // check that the interface is not a class
176  auto interfaceDecl = dyn_cast<ast::InterfaceDecl>(interface->decl());
177  if(!interfaceDecl) {
178  diag.ReportError(classDecl->location())
179  << "A class must not implement a class" << classDecl->name();
180  } else {
181  inheritanceMap_[classDecl].insert(interfaceDecl);
182  }
183  }
184  } else if(auto interfaceDecl = dyn_cast<ast::InterfaceDecl>(body)) {
185  // no duplicate interfaces
186  for(auto extends : interfaceDecl->extends()) {
187  for(auto other : interfaceDecl->extends()) {
188  if(extends == other) continue;
189  if(extends->decl() == other->decl()) {
190  diag.ReportError(interfaceDecl->location())
191  << "A interface must not extend the same interface twice. "
192  << interfaceDecl->name();
193  }
194  }
195  // check that the interface is not a class
196  auto superInterface = dyn_cast<ast::InterfaceDecl>(extends->decl());
197  if(!superInterface) {
198  diag.ReportError(superInterface->location())
199  << "A interface must not extend a class"
200  << superInterface->name();
201  } else {
202  inheritanceMap_[interfaceDecl].insert(superInterface);
203  }
204  // print debug information
205  if(diag.Verbose(2)) {
206  diag.ReportDebug(2)
207  << "Interface: " << interfaceDecl->name() << " extends "
208  << superInterface->name() << "\n";
209  }
210  }
211  }
212  }
213  checkMethodInheritance();
214 }
215 
216 void HierarchyChecker::checkClassMethod(
217  ast::ClassDecl const* classDecl,
218  std::pmr::vector<ast::MethodDecl const*>& inheritedMethods) {
219  std::pmr::vector<ast::MethodDecl const*> allMethods;
220  std::pmr::vector<ast::MethodDecl const*> inheritedNotOverriden;
221  // check for duplicate methods
222  for(auto method : classDecl->methods()) {
223  allMethods.emplace_back(method);
224  for(auto other : classDecl->methods()) {
225  if(method == other) continue;
226  if(isSameMethodSignature(method, other)) {
227  diag.ReportError(method->location())
228  << "A class must not declare two methods with the same "
229  "signature. "
230  << method->name();
231  }
232  }
233  }
234 
235  // check for abstract declaration
236  for(auto method : classDecl->methods()) {
237  if(method->modifiers().isAbstract() &&
238  !classDecl->modifiers().isAbstract()) {
239  diag.ReportError(classDecl->location())
240  << "A class that contains (declares or inherits) any "
241  "abstract methods must be abstract. "
242  << classDecl->name();
243  break;
244  }
245  }
246 
247  // check for method replacement
248  for(auto const* other : inheritedMethods) {
249  bool isOverriden = false;
250  for(auto const* method : classDecl->methods()) {
251  if(!isSameMethodSignature(method, other)) continue;
252  isOverriden = true;
253  if(method->returnTy() != other->returnTy()) {
254  diag.ReportError(classDecl->location())
255  << "A method must not replace a method with a "
256  "different return type. "
257  << other->name();
258  }
259  if(!method->modifiers().isStatic() && other->modifiers().isStatic()) {
260  diag.ReportError(classDecl->location())
261  << "A nonstatic method must not replace a static "
262  "method. "
263  << other->name();
264  }
265  if(method->modifiers().isStatic() && !other->modifiers().isStatic()) {
266  diag.ReportError(classDecl->location())
267  << "A static method must not replace a nonstatic "
268  "method. "
269  << other->name();
270  }
271  if(method->modifiers().isProtected() && other->modifiers().isPublic()) {
272  diag.ReportError(classDecl->location())
273  << "A protected method must not replace a public "
274  "method. "
275  << other->name();
276  }
277  if(other->modifiers().isFinal()) {
278  diag.ReportError(classDecl->location())
279  << "A method must not replace a final method. " << other->name();
280  }
281  }
282  if(!isOverriden) inheritedNotOverriden.emplace_back(other);
283  }
284 
285  // check for abstract method implementation
286  for(auto method : inheritedNotOverriden) {
287  bool isImplemented = !method->modifiers().isAbstract();
288  for(auto other : inheritedNotOverriden) {
289  if(isSameMethodSignature(method, other)) {
290  if(method->returnTy() != other->returnTy()) {
291  diag.ReportError(other->location())
292  << "A method must not replace a method with a "
293  "different return type. "
294  << other->name();
295  } else if(!other->modifiers().isAbstract()) {
296  if(other->modifiers().isProtected() &&
297  method->modifiers().isPublic()) {
298  diag.ReportError(other->location())
299  << "A protected method must not replace a public "
300  "method. "
301  << other->name();
302  }
303  isImplemented = true;
304  }
305  }
306  }
307  if(!isImplemented && !classDecl->modifiers().isAbstract()) {
308  diag.ReportError(classDecl->location())
309  << "an abstract method must be implemented in a "
310  "non-abstract class "
311  << method->name()
312  << classDecl->location()
313  << "does not implement " << method->name()
314  << cast<ast::Decl>(method->parent())->location()
315  << "method is inherited from here"
316  << method->location()
317  << "abstract method is declared here";
318  } else if(isImplemented == !method->modifiers().isAbstract()) {
319  allMethods.emplace_back(method);
320  }
321  }
322  // record the inherited methods
323  setInheritedMethods(classDecl, allMethods);
324 
325  // print debug information
326  if(diag.Verbose(2)) {
327  diag.ReportDebug(2) << "Class: " << classDecl->name();
328  diag.ReportDebug(2) << "Inherited methods: ";
329  for(auto method : allMethods) {
330  if (auto parent = dyn_cast<ast::ClassDecl>(method->parent())) {
331  diag.ReportDebug(2) << "\t" << method->name() << " -> " << parent->name();
332  } else if (auto parent = dyn_cast<ast::InterfaceDecl>(method->parent())) {
333  diag.ReportDebug(2) << "\t" << method->name() << " -> " << parent->name();
334  }
335  }
336  }
337 }
338 
339 void HierarchyChecker::checkClassConstructors(ast::ClassDecl const* classDecl) {
340  for(auto constructor : classDecl->constructors()) {
341  for(auto other : classDecl->constructors()) {
342  if(constructor == other) continue;
343  if(isSameMethodSignature(constructor, other)) {
344  diag.ReportError(constructor->location())
345  << "A class must not declare two constructors with the same "
346  "signature. "
347  << classDecl->name();
348  }
349  }
350  }
351 }
352 
353 void HierarchyChecker::checkInterfaceMethod(
354  ast::InterfaceDecl const* interfaceDecl,
355  std::pmr::vector<ast::MethodDecl const*>& inheritedMethods) {
356  std::pmr::vector<ast::MethodDecl const*> allMethods;
357 
358  for(auto method : interfaceDecl->methods()) {
359  allMethods.emplace_back(method);
360  for(auto other : interfaceDecl->methods()) {
361  if(method == other) continue;
362  if(isSameMethodSignature(method, other)) {
363  diag.ReportError(method->location())
364  << "An interface must not declare two methods with the same "
365  "signature. "
366  << method->name();
367  }
368  }
369  }
370 
371  // for some reason we have to check against the object class
372  auto objectClass =
373  cast<ast::ClassDecl>(interfaceDecl->objectSuperclass()->decl());
374  for(auto method : interfaceDecl->methods()) {
375  for(auto other : objectClass->methods()) {
376  if(!isSameMethodSignature(method, other)) continue;
377  if(method->returnTy() != other->returnTy()) {
378  diag.ReportError(interfaceDecl->location())
379  << "A method must not replace a method with a "
380  "different return type. "
381  << other->name();
382  }
383  if(method->modifiers().isProtected() && other->modifiers().isPublic()) {
384  diag.ReportError(interfaceDecl->location())
385  << "A protected method must not replace a public "
386  "method. "
387  << other->name();
388  }
389  if(other->modifiers().isFinal()) {
390  diag.ReportError(interfaceDecl->location())
391  << "A method must not replace a final method. " << other->name();
392  }
393  }
394  }
395 
396  for(auto method : inheritedMethods) {
397  bool isOverriden = false;
398  for(auto other : interfaceDecl->methods()) {
399  if(isSameMethodSignature(method, other)) {
400  if(method->returnTy() != other->returnTy()) {
401  diag.ReportError(method->location())
402  << "An interface must not contain two methods with the same "
403  "signature. "
404  << method->name();
405  } else {
406  isOverriden = true;
407  }
408  }
409  }
410  if(!isOverriden) allMethods.emplace_back(method);
411  }
412 
413  for(auto method : allMethods) {
414  for(auto other : allMethods) {
415  if(method == other) continue;
416  if(isSameMethodSignature(method, other) &&
417  method->returnTy() != other->returnTy()) {
418  diag.ReportError(method->location())
419  << "An interface must not contain two methods with the same "
420  "signature. "
421  << method->name();
422  }
423  }
424  }
425 
426  // record the inherited methods
427  setInheritedMethods(interfaceDecl, allMethods);
428 
429  // print debug information
430  if(diag.Verbose(2)) {
431  diag.ReportDebug(2) << "Interface: " << interfaceDecl->name();
432  diag.ReportDebug(2) << "Inherited methods:";
433  for(auto method : allMethods) diag.ReportDebug(2) << "\t" << method->name();
434  }
435 }
436 
437 } // namespace semantic