1 #include "semantic/ExprStaticChecker.h"
5 #include "ast/DeclContext.h"
6 #include "ast/ExprNode.h"
8 #include "diagnostics/Diagnostics.h"
9 #include "utils/Utils.h"
13 using ETy = ExprStaticCheckerData;
14 using ESC = ExprStaticChecker;
15 namespace ex = ast::exprnode;
16 using namespace diagnostics;
19 static bool isDeclStatic(ast::
Decl const* decl) {
20 if(
auto* field = dyn_cast<ast::FieldDecl>(decl)) {
21 return field->modifiers().isStatic();
22 }
else if(
auto* method = dyn_cast<ast::MethodDecl>(decl)) {
23 return method->modifiers().isStatic();
28 void ESC::Evaluate(ast::
Expr* expr, ExprStaticCheckerState state) {
30 loc_ = expr->location();
31 ETy single =
this->EvaluateList(expr->list());
33 checkInstanceVar(single);
36 ETy ESC::mapValue(ExprValue& node)
const {
38 if(dyn_cast<ex::ThisNode>(node) && state.isStaticContext) {
39 throw diag.ReportError(node.location())
40 <<
"cannot use 'this' in a static context";
46 assert(dyn_cast<ex::MethodName>(node) || node.isTypeResolved());
47 if(dyn_cast<ex::LiteralNode>(node)) {
48 return ETy{
nullptr, node.type(),
true,
false};
49 }
else if(dyn_cast<ex::TypeNode>(node)) {
50 return ETy{
nullptr, node.type(),
false,
false};
52 bool isParentClass = dyn_cast<ast::ClassDecl>(node.decl()
->parent());
53 bool isStaticModifier = isDeclStatic(node.decl());
54 bool isInstanceVar = isParentClass && !isStaticModifier;
55 return ETy{node.decl(), node.type(),
true, isInstanceVar};
59 ETy ESC::evalBinaryOp(BinaryOp& op, ETy lhs, ETy rhs)
const {
61 if(op.opType() == ast::exprnode::BinaryOp::OpType::Assignment) {
63 checkInstanceVar(lhs,
false);
65 if(
auto field = dyn_cast_or_null<ast::FieldDecl>(lhs.decl)) {
66 if(field->modifiers().isFinal()) {
67 throw diag.ReportError(loc_) <<
"cannot assign to a final field";
69 }
else if (
auto var = dyn_cast_or_null<ast::VarDecl>(lhs.decl)) {
70 op.setVarAssigned(var);
73 checkInstanceVar(lhs,
true);
75 checkInstanceVar(rhs);
76 return ETy{
nullptr, op.resultType(),
true,
false};
79 ETy ESC::evalUnaryOp(UnaryOp& op, ETy val)
const {
81 checkInstanceVar(val);
82 return ETy{
nullptr, op.resultType(),
true,
false};
85 ETy ESC::evalMemberAccess(DotOp& op, ETy lhs, ETy field)
const {
90 assert(field.isValue && field.decl);
92 checkInstanceVar(lhs);
93 isAccessible(lhs, field);
95 if(isDeclStatic(field.decl)) {
96 throw diag.ReportError(loc_)
97 <<
"cannot access a static field through an instance variable"
98 << argLocation(1) <<
"field is static";
101 return ETy{field.decl, op.resultType(),
true,
false};
104 ETy ESC::evalMethodCall(MethodOp& op, ETy method,
const op_array& args)
const {
106 assert(method.isValue && method.decl);
108 checkInstanceVar(method);
110 for(
auto arg : args) {
112 checkInstanceVar(arg);
115 return ETy{
nullptr, op.resultType(),
true,
false};
118 ETy ESC::evalNewObject(NewOp& op, ETy ty,
const op_array& args)
const {
119 assert(!ty.isValue && ty.type);
120 for(
auto arg : args) {
122 checkInstanceVar(arg);
125 return ETy{
nullptr, op.resultType(),
true,
false};
128 ETy ESC::evalNewArray(NewArrayOp& op, ETy type, ETy size)
const {
129 assert(!type.isValue && type.type);
131 checkInstanceVar(size);
133 return ETy{
nullptr, op.resultType(),
true,
false};
136 ETy ESC::evalArrayAccess(ArrayAccessOp& op, ETy arr, ETy idx)
const {
139 checkInstanceVar(arr);
140 checkInstanceVar(idx);
142 return ETy{
nullptr, op.resultType(),
true,
false};
145 ETy ESC::evalCast(CastOp& op, ETy type, ETy obj)
const {
146 assert(!type.isValue && type.type);
149 checkInstanceVar(obj);
150 return ETy{
nullptr, op.resultType(),
true,
false};
153 void ESC::isAccessible(ETy lhs, ETy var)
const {
154 if(!var.isInstanceVar)
return;
155 auto lhsVar = dyn_cast_or_null<ast::
VarDecl>(lhs.decl);
157 auto lhsRef = dyn_cast<ast::ReferenceType>(lhsVar->type());
159 auto lhsClass = dyn_cast<ast::ClassDecl>(lhsRef->decl());
160 if(!lhsClass)
return;
161 if(
auto method = dyn_cast<ast::MethodDecl>(var.decl)) {
162 if(method->modifiers().isProtected()) {
163 auto cur_cu = dyn_cast<ast::CompilationUnit>(state.currentClass
->parent());
164 auto other_cu = dyn_cast<ast::CompilationUnit>(lhsClass->parent());
165 if(!HC.isSuperClass(state.currentClass, lhsClass)
166 && cur_cu->getPackageName() != other_cu->getPackageName()) {
167 throw diag.ReportError(loc_)
168 <<
"cannot access protected method" << argLocation(0)
169 <<
"instance of " << lhsClass->name() << argLocation(1)
170 <<
"protected member";
173 }
else if(
auto field = dyn_cast<ast::FieldDecl>(var.decl)) {
174 if(field->modifiers().isProtected()) {
175 auto cur_cu = dyn_cast<ast::CompilationUnit>(state.currentClass
->parent());
176 auto other_cu = dyn_cast<ast::CompilationUnit>(lhsClass->parent());
177 if(!HC.isSuperClass(state.currentClass, lhsClass)
178 && cur_cu->getPackageName() != other_cu->getPackageName()) {
179 throw diag.ReportError(loc_)
180 <<
"cannot access protected field" << argLocation(0)
181 <<
"instance of " << lhsClass->name() << argLocation(1)
182 <<
"protected member";
189 void ESC::checkInstanceVar(ETy var,
bool checkInitOrder)
const {
190 if(!var.isInstanceVar)
return;
192 if(state.isStaticContext) {
193 throw diag.ReportError(loc_)
194 <<
"cannot access or invoke instance members in a static context";
198 if(state.isInstFieldInitializer && checkInitOrder) {
199 auto fieldDecl = cast<ast::FieldDecl>(var.decl);
200 if(!state.fieldScope->canView(fieldDecl->scope())) {
201 throw diag.ReportError(loc_) <<
"cannot access instance members in "
202 "initializer before they are defined";