Joos1W Compiler Framework
All Classes Functions Typedefs Pages
AstValidator.cc
1 #include "semantic/AstValidator.h"
2 
3 #include <cassert>
4 #include <utility>
5 
6 #include "ast/Decl.h"
7 #include "ast/ExprNode.h"
8 #include "ast/Stmt.h"
9 #include "semantic/ExprTypeResolver.h"
10 #include "utils/Utils.h"
11 
12 namespace semantic {
13 
14 void AstChecker::ValidateLU(const ast::LinkingUnit& LU) {
15  cu_ = nullptr;
16  for(auto* cu : LU.compliationUnits()) {
17  validateCU(*cu);
18  }
19 }
20 
21 void AstChecker::validateCU(const ast::CompilationUnit& CU) {
22  cu_ = &CU;
23  if(!CU.bodyAsDecl()) return;
24  for(auto* child : CU.bodyAsDecl()->children()) {
25  if(auto* method = dyn_cast_or_null<ast::MethodDecl>(child)) {
26  validateMethod(*method);
27  } else if(auto* field = dyn_cast_or_null<ast::FieldDecl>(child)) {
28  valdiateTypedDecl(*field);
29  }
30  }
31 }
32 
33 void AstChecker::validateMethod(const ast::MethodDecl& method) {
34  currentMethod = &method;
35  if(method.body()) validateStmt(*method.body());
36  currentMethod = nullptr;
37 }
38 
39 void AstChecker::validateStmt(const ast::Stmt& stmt) {
40  if(auto ret = dyn_cast<ast::ReturnStmt>(stmt)) {
41  validateReturnStmt(*ret);
42  } else if(auto decl = dyn_cast<ast::DeclStmt>(stmt)) {
43  valdiateTypedDecl(*decl->decl());
44  } else if(auto ifstmt = dyn_cast<ast::IfStmt>(stmt)) {
45  assert(ifstmt->condition());
46  auto* condTy = getTypeFromExpr(ifstmt->condition());
47  if(!condTy || !condTy->isBoolean()) {
48  diag.ReportError(ifstmt->condition()->location())
49  << "if condition expression must be yield a boolean";
50  }
51  } else if(auto forstmt = dyn_cast<ast::ForStmt>(stmt)) {
52  if (forstmt->condition()) {
53  auto* condTy = getTypeFromExpr(forstmt->condition());
54  if(!condTy || !condTy->isBoolean()) {
55  diag.ReportError(forstmt->condition()->location())
56  << "for condition expression must be yield a boolean";
57  }
58  }
59  } else if(auto whilestmt = dyn_cast<ast::WhileStmt>(stmt)) {
60  assert(whilestmt->condition());
61  auto* condTy = getTypeFromExpr(whilestmt->condition());
62  if(!condTy || !condTy->isBoolean()) {
63  diag.ReportError(whilestmt->condition()->location())
64  << "while condition expression must be yield a boolean";
65  }
66  }
67  for(auto* child : stmt.children()) {
68  if(auto* stmt = dyn_cast_or_null<ast::Stmt>(child)) {
69  validateStmt(*stmt);
70  }
71  }
72 }
73 
74 ast::Type const* AstChecker::getTypeFromExpr(ast::Expr const* expr) const {
75  assert(expr);
76  auto lastExpr = expr->list().tail();
77  assert(lastExpr);
78  if(auto op = dyn_cast<ast::exprnode::ExprOp>(lastExpr)) {
79  return op->resultType();
80  } else if(auto value = dyn_cast<ast::exprnode::ExprValue>(lastExpr)) {
81  assert(value->type() && "Expr value type cannot be null");
82  return value->type();
83  }
84  std::unreachable();
85 }
86 
87 void AstChecker::valdiateTypedDecl(const ast::TypedDecl& decl) {
88  if(!decl.hasInit()) return;
89  auto* declTy = decl.type();
90  auto* exprTy = getTypeFromExpr(decl.init());
91  if(!exprTy) {
92  diag.ReportError(decl.init()->location())
93  << "initializer type cannot be void";
94  return;
95  }
96  if(!exprTypeResolver.isAssignableTo(declTy, exprTy)) {
97  diag.ReportError(decl.location())
98  << "initializer type must be assignable to declared type";
99  }
100 }
101 
102 void AstChecker::validateReturnStmt(const ast::ReturnStmt& ret) {
103  assert(currentMethod);
104  auto methodRetTy = currentMethod->returnTy().type;
105  if(!ret.expr()) {
106  if(methodRetTy) {
107  diag.ReportError(ret.location())
108  << "return must be non-void" << ret.location() << "return is here"
109  << currentMethod->location() << "method declared here";
110  }
111  return;
112  }
113 
114  ast::Type const* retTy = getTypeFromExpr(ret.expr());
115  if(!retTy) {
116  diag.ReportError(ret.expr()->location())
117  << "return expression cannot be void, regardless of method return "
118  "type";
119  return;
120  }
121 
122  if(!methodRetTy) {
123  if(retTy) {
124  diag.ReportError(ret.location())
125  << "return must be void" << ret.location() << "return is here"
126  << currentMethod->location() << "method declared here";
127  }
128  return;
129  }
130 
131  if(!exprTypeResolver.isAssignableTo(methodRetTy, retTy)) {
132  diag.ReportError(ret.location())
133  << "return type must be assignable to method return type"
134  << ret.location() << "return is type " << retTy->toString()
135  << currentMethod->location() << "method declared here";
136  }
137 }
138 
139 } // namespace semantic