Add l-value checking in typechecker
This commit is contained in:
parent
d79151380f
commit
6dad36e34a
@ -125,7 +125,7 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { this->m_ty, false };
|
return { this->m_ty, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringLiteralExpression::typecheck_preprocess(typecheck::Scope&) {}
|
void StringLiteralExpression::typecheck_preprocess(typecheck::Scope&) {}
|
||||||
@ -139,7 +139,7 @@ namespace AST {
|
|||||||
new types::FundamentalType{ types::FundamentalTypeKind::Char }
|
new types::FundamentalType{ types::FundamentalTypeKind::Char }
|
||||||
};
|
};
|
||||||
auto ptr_ty = new types::ArrayType{ char_ty, static_cast<uint32_t>(this->m_value.size()) + 1, true };
|
auto ptr_ty = new types::ArrayType{ char_ty, static_cast<uint32_t>(this->m_value.size()) + 1, true };
|
||||||
return { std::shared_ptr<types::Type>{ptr_ty}, true };
|
return { std::shared_ptr<types::Type>{ptr_ty}, true, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
void ValueReferenceExpression::typecheck_preprocess(typecheck::Scope&) {}
|
void ValueReferenceExpression::typecheck_preprocess(typecheck::Scope&) {}
|
||||||
@ -150,13 +150,13 @@ namespace AST {
|
|||||||
std::optional<std::shared_ptr<types::Type>>
|
std::optional<std::shared_ptr<types::Type>>
|
||||||
) {
|
) {
|
||||||
if (scope.symbols.find(this->m_name) != scope.symbols.end()) {
|
if (scope.symbols.find(this->m_name) != scope.symbols.end()) {
|
||||||
return { scope.symbols[this->m_name], false };
|
return { scope.symbols[this->m_name], false, true };
|
||||||
}
|
}
|
||||||
|
|
||||||
state.errors.push_back(CompileError("Value " + this->m_name + " not defined", this->m_meta));
|
state.errors.push_back(CompileError("Value " + this->m_name + " not defined", this->m_meta));
|
||||||
return { std::shared_ptr<types::Type>{
|
return { std::shared_ptr<types::Type>{
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, true };
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryOperationExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void BinaryOperationExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -169,15 +169,23 @@ namespace AST {
|
|||||||
typecheck::Scope& scope,
|
typecheck::Scope& scope,
|
||||||
std::optional<std::shared_ptr<types::Type>> expected_ty
|
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||||
) {
|
) {
|
||||||
auto lhs_ty = this->m_lhs->typecheck(state, scope, {}).type;
|
auto lhs_res = this->m_lhs->typecheck(state, scope, {});
|
||||||
|
auto lhs_ty = lhs_res.type;
|
||||||
auto rhs_ty = this->m_rhs->typecheck(state, scope, {}).type;
|
auto rhs_ty = this->m_rhs->typecheck(state, scope, {}).type;
|
||||||
|
|
||||||
if (this->m_binop == types::BinOp::Assignment) {
|
if (this->m_binop == types::BinOp::Assignment) {
|
||||||
|
if (!lhs_res.lvalue) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
else if (lhs_ty->m_kind == types::TypeKind::Array) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
|
||||||
// Re-typecheck rhs to actually match lhs
|
// Re-typecheck rhs to actually match lhs
|
||||||
auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty).type;
|
auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty).type;
|
||||||
auto rhs_ty_res = check_type(state, rhs_ty, lhs_ty);
|
auto rhs_ty_res = check_type(state, rhs_ty, lhs_ty);
|
||||||
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
|
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
|
||||||
return { lhs_ty, false };
|
return { lhs_ty, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find a binop that matches exactly
|
// Try to find a binop that matches exactly
|
||||||
@ -189,7 +197,7 @@ namespace AST {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (binop) {
|
if (binop) {
|
||||||
return { binop->result(*binop, lhs_ty, rhs_ty), false };
|
return { binop->result(*binop, lhs_ty, rhs_ty), false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// If that fails, try to find binop that matches on one side perfectly
|
// If that fails, try to find binop that matches on one side perfectly
|
||||||
@ -209,7 +217,7 @@ namespace AST {
|
|||||||
// Skip if not implicitly castable to lhs
|
// Skip if not implicitly castable to lhs
|
||||||
continue;
|
continue;
|
||||||
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
|
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
|
||||||
return { binop.result(binop, lhs_ty, rhs_ty), false };
|
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
|
||||||
}
|
}
|
||||||
else if (types::types_equal(binop.rhs, rhs_ty)) {
|
else if (types::types_equal(binop.rhs, rhs_ty)) {
|
||||||
auto lhs_res = check_type(state, lhs_ty, binop.lhs);
|
auto lhs_res = check_type(state, lhs_ty, binop.lhs);
|
||||||
@ -217,7 +225,7 @@ namespace AST {
|
|||||||
// Skip if not implicitly castable to rhs
|
// Skip if not implicitly castable to rhs
|
||||||
continue;
|
continue;
|
||||||
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
|
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
|
||||||
return { binop.result(binop, lhs_ty, rhs_ty), false };
|
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +243,7 @@ namespace AST {
|
|||||||
auto rhs_result = check_type(state, rhs_ty, binop.rhs);
|
auto rhs_result = check_type(state, rhs_ty, binop.rhs);
|
||||||
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_result, state);
|
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_result, state);
|
||||||
this->m_rhs = handle_res(std::move(this->m_rhs), lhs_result, state);
|
this->m_rhs = handle_res(std::move(this->m_rhs), lhs_result, state);
|
||||||
return { binop.result(binop, lhs_ty, rhs_ty), false };
|
return { binop.result(binop, lhs_ty, rhs_ty), false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// No suitable binops found :(
|
// No suitable binops found :(
|
||||||
@ -247,7 +255,7 @@ namespace AST {
|
|||||||
this->m_meta));
|
this->m_meta));
|
||||||
|
|
||||||
return { std::shared_ptr<types::Type>{
|
return { std::shared_ptr<types::Type>{
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void } }, false };
|
new types::FundamentalType{ types::FundamentalTypeKind::Void } }, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionCallExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void FunctionCallExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -268,7 +276,7 @@ namespace AST {
|
|||||||
state.errors.push_back(CompileError("Tried calling a non-function", this->m_meta));
|
state.errors.push_back(CompileError("Tried calling a non-function", this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fn_ty = dynamic_cast<types::FunctionType*>(expr_ty.get());
|
auto fn_ty = dynamic_cast<types::FunctionType*>(expr_ty.get());
|
||||||
@ -294,7 +302,7 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { fn_ty->m_ret_ty, false };
|
return { fn_ty->m_ret_ty, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
void CastExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void CastExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -310,14 +318,14 @@ namespace AST {
|
|||||||
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
||||||
auto cast = types::find_cast(state.casts, expr_ty, this->m_ty);
|
auto cast = types::find_cast(state.casts, expr_ty, this->m_ty);
|
||||||
if (cast) {
|
if (cast) {
|
||||||
return { cast->target_ty, false };
|
return { cast->target_ty, false, false };
|
||||||
}
|
}
|
||||||
state.errors.push_back(CompileError("Cast from type "
|
state.errors.push_back(CompileError("Cast from type "
|
||||||
+ expr_ty->formatted() + "to type " + this->m_ty->formatted()
|
+ expr_ty->formatted() + "to type " + this->m_ty->formatted()
|
||||||
+ " is not permitted", this->m_meta));
|
+ " is not permitted", this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> { new types::FundamentalType{
|
return { std::shared_ptr<types::Type> { new types::FundamentalType{
|
||||||
types::FundamentalTypeKind::Void
|
types::FundamentalTypeKind::Void
|
||||||
} }, false };
|
} }, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void RefExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -332,7 +340,7 @@ namespace AST {
|
|||||||
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::PointerType{ expr_ty }
|
new types::PointerType{ expr_ty }
|
||||||
}, false };
|
}, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerefExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void DerefExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -344,16 +352,16 @@ namespace AST {
|
|||||||
typecheck::Scope& scope,
|
typecheck::Scope& scope,
|
||||||
std::optional<std::shared_ptr<types::Type>>
|
std::optional<std::shared_ptr<types::Type>>
|
||||||
) {
|
) {
|
||||||
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
auto expr_ty = this->m_expr->typecheck(state, scope, {});
|
||||||
if (expr_ty->m_kind != types::TypeKind::Pointer) {
|
if (expr_ty.type->m_kind != types::TypeKind::Pointer) {
|
||||||
state.errors.push_back(
|
state.errors.push_back(
|
||||||
CompileError("Tried to deref " + expr_ty->formatted(), this->m_meta));
|
CompileError("Tried to deref " + expr_ty.type->formatted(), this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.get());
|
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.type.get());
|
||||||
return { ptr_ty->m_inner, false };
|
return { ptr_ty->m_inner, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void IndexAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -365,27 +373,27 @@ namespace AST {
|
|||||||
typecheck::Scope& scope,
|
typecheck::Scope& scope,
|
||||||
std::optional<std::shared_ptr<types::Type>>
|
std::optional<std::shared_ptr<types::Type>>
|
||||||
) {
|
) {
|
||||||
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
auto expr_ty = this->m_expr->typecheck(state, scope, {});
|
||||||
if (expr_ty->m_kind != types::TypeKind::Pointer && expr_ty->m_kind != types::TypeKind::Array) {
|
if (expr_ty.type->m_kind != types::TypeKind::Pointer && expr_ty.type->m_kind != types::TypeKind::Array) {
|
||||||
state.errors.push_back(
|
state.errors.push_back(
|
||||||
CompileError("Tried to index " + expr_ty->formatted(), this->m_meta));
|
CompileError("Tried to index " + expr_ty.type->formatted(), this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
if (expr_ty->m_kind == types::TypeKind::Pointer) {
|
if (expr_ty.type->m_kind == types::TypeKind::Pointer) {
|
||||||
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.get());
|
auto ptr_ty = dynamic_cast<types::PointerType*>(expr_ty.type.get());
|
||||||
return { ptr_ty->m_inner, false };
|
return { ptr_ty->m_inner, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
else if (expr_ty->m_kind == types::TypeKind::Array) {
|
else if (expr_ty.type->m_kind == types::TypeKind::Array) {
|
||||||
auto ptr_ty = dynamic_cast<types::ArrayType*>(expr_ty.get());
|
auto ptr_ty = dynamic_cast<types::ArrayType*>(expr_ty.type.get());
|
||||||
return { ptr_ty->m_inner, false };
|
return { ptr_ty->m_inner, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default return type
|
// Default return type
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void FieldAccessExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -397,32 +405,32 @@ namespace AST {
|
|||||||
typecheck::Scope& scope,
|
typecheck::Scope& scope,
|
||||||
std::optional<std::shared_ptr<types::Type>>
|
std::optional<std::shared_ptr<types::Type>>
|
||||||
) {
|
) {
|
||||||
auto expr_ty = this->m_expr->typecheck(state, scope, {}).type;
|
auto expr_ty = this->m_expr->typecheck(state, scope, {});
|
||||||
if (expr_ty->m_kind != types::TypeKind::Struct) {
|
if (expr_ty.type->m_kind != types::TypeKind::Struct) {
|
||||||
state.errors.push_back(
|
state.errors.push_back(
|
||||||
CompileError("Tried to access " + expr_ty->formatted() + "." + this->m_field, this->m_meta));
|
CompileError("Tried to access " + expr_ty.type->formatted() + "." + this->m_field, this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto struct_ty = dynamic_cast<types::StructType*>(expr_ty.get());
|
auto struct_ty = dynamic_cast<types::StructType*>(expr_ty.type.get());
|
||||||
if (struct_ty->m_fields) {
|
if (struct_ty->m_fields) {
|
||||||
for (auto& field : *struct_ty->m_fields) {
|
for (auto& field : *struct_ty->m_fields) {
|
||||||
if (field.first == this->m_field) {
|
if (field.first == this->m_field) {
|
||||||
return { field.second, false };
|
return { field.second, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.errors.push_back(CompileError("No such field", this->m_meta));
|
state.errors.push_back(CompileError("No such field", this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
|
|
||||||
state.errors.push_back(CompileError("Cannot access fields of opaque struct", this->m_meta));
|
state.errors.push_back(CompileError("Cannot access fields of opaque struct", this->m_meta));
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, false };
|
}, false, expr_ty.lvalue };
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListInitializerExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
void ListInitializerExpression::typecheck_preprocess(typecheck::Scope& scope) {
|
||||||
@ -462,7 +470,7 @@ namespace AST {
|
|||||||
state.errors.push_back(CompileError(
|
state.errors.push_back(CompileError(
|
||||||
"Too many initializer values for " + struct_ty->formatted(),
|
"Too many initializer values for " + struct_ty->formatted(),
|
||||||
this->m_meta));
|
this->m_meta));
|
||||||
return { *expected_ty, true };
|
return { *expected_ty, true, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < static_cast<int>(this->m_expressions.size()); i++) {
|
for (int i = 0; i < static_cast<int>(this->m_expressions.size()); i++) {
|
||||||
@ -473,18 +481,18 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->m_ty = *expected_ty;
|
this->m_ty = *expected_ty;
|
||||||
return { this->m_ty, true };
|
return { this->m_ty, true, false };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (this->m_expressions.size() > 0) {
|
if (this->m_expressions.size() > 0) {
|
||||||
state.errors.push_back(CompileError(
|
state.errors.push_back(CompileError(
|
||||||
"Too many initializer values for " + struct_ty->formatted(),
|
"Too many initializer values for " + struct_ty->formatted(),
|
||||||
this->m_meta));
|
this->m_meta));
|
||||||
return { *expected_ty, true };
|
return { *expected_ty, true, false };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this->m_ty = *expected_ty;
|
this->m_ty = *expected_ty;
|
||||||
return { this->m_ty, true };
|
return { this->m_ty, true, false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,10 +500,10 @@ namespace AST {
|
|||||||
else {
|
else {
|
||||||
return { std::shared_ptr<types::Type> {
|
return { std::shared_ptr<types::Type> {
|
||||||
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
new types::FundamentalType{ types::FundamentalTypeKind::Void }
|
||||||
}, true };
|
}, true, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { this->m_ty, true };
|
return { this->m_ty, true, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// No expected ty, try to infer array type from elements
|
// No expected ty, try to infer array type from elements
|
||||||
@ -508,7 +516,7 @@ namespace AST {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return { this->m_ty, true };
|
return { this->m_ty, true, false };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto first_expr_ty = this->m_expressions[0]->typecheck(state, scope, {}).type;
|
auto first_expr_ty = this->m_expressions[0]->typecheck(state, scope, {}).type;
|
||||||
@ -524,7 +532,7 @@ namespace AST {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return { this->m_ty, true };
|
return { this->m_ty, true, false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ namespace typecheck {
|
|||||||
struct ExpressionType {
|
struct ExpressionType {
|
||||||
std::shared_ptr<types::Type> type;
|
std::shared_ptr<types::Type> type;
|
||||||
bool array_initializer;
|
bool array_initializer;
|
||||||
|
bool lvalue;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user