diff --git a/src/typechecker.cpp b/src/typechecker.cpp index 2eaeb08..ce7b976 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -125,7 +125,7 @@ namespace AST { } } - return { this->m_ty, false }; + return { this->m_ty, false, false }; } void StringLiteralExpression::typecheck_preprocess(typecheck::Scope&) {} @@ -139,7 +139,7 @@ namespace AST { new types::FundamentalType{ types::FundamentalTypeKind::Char } }; auto ptr_ty = new types::ArrayType{ char_ty, static_cast(this->m_value.size()) + 1, true }; - return { std::shared_ptr{ptr_ty}, true }; + return { std::shared_ptr{ptr_ty}, true, false }; } void ValueReferenceExpression::typecheck_preprocess(typecheck::Scope&) {} @@ -150,13 +150,13 @@ namespace AST { std::optional> ) { 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)); return { std::shared_ptr{ new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, true }; } void BinaryOperationExpression::typecheck_preprocess(typecheck::Scope& scope) { @@ -169,15 +169,23 @@ namespace AST { typecheck::Scope& scope, std::optional> 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; 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 auto rhs_ty = this->m_rhs->typecheck(state, scope, lhs_ty).type; 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); - return { lhs_ty, false }; + return { lhs_ty, false, false }; } // Try to find a binop that matches exactly @@ -189,7 +197,7 @@ namespace AST { ); 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 @@ -209,7 +217,7 @@ namespace AST { // Skip if not implicitly castable to lhs continue; 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)) { auto lhs_res = check_type(state, lhs_ty, binop.lhs); @@ -217,7 +225,7 @@ namespace AST { // Skip if not implicitly castable to rhs continue; 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); 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); - return { binop.result(binop, lhs_ty, rhs_ty), false }; + return { binop.result(binop, lhs_ty, rhs_ty), false, false }; } // No suitable binops found :( @@ -247,7 +255,7 @@ namespace AST { this->m_meta)); return { std::shared_ptr{ - new types::FundamentalType{ types::FundamentalTypeKind::Void } }, false }; + new types::FundamentalType{ types::FundamentalTypeKind::Void } }, false, false }; } 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)); return { std::shared_ptr { new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, false }; } auto fn_ty = dynamic_cast(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) { @@ -310,14 +318,14 @@ namespace AST { auto expr_ty = this->m_expr->typecheck(state, scope, {}).type; auto cast = types::find_cast(state.casts, expr_ty, this->m_ty); if (cast) { - return { cast->target_ty, false }; + return { cast->target_ty, false, false }; } state.errors.push_back(CompileError("Cast from type " + expr_ty->formatted() + "to type " + this->m_ty->formatted() + " is not permitted", this->m_meta)); return { std::shared_ptr { new types::FundamentalType{ types::FundamentalTypeKind::Void - } }, false }; + } }, false, false }; } void RefExpression::typecheck_preprocess(typecheck::Scope& scope) { @@ -332,7 +340,7 @@ namespace AST { auto expr_ty = this->m_expr->typecheck(state, scope, {}).type; return { std::shared_ptr { new types::PointerType{ expr_ty } - }, false }; + }, false, false }; } void DerefExpression::typecheck_preprocess(typecheck::Scope& scope) { @@ -344,16 +352,16 @@ namespace AST { typecheck::Scope& scope, std::optional> ) { - auto expr_ty = this->m_expr->typecheck(state, scope, {}).type; - if (expr_ty->m_kind != types::TypeKind::Pointer) { + auto expr_ty = this->m_expr->typecheck(state, scope, {}); + if (expr_ty.type->m_kind != types::TypeKind::Pointer) { 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 { new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, expr_ty.lvalue }; } - auto ptr_ty = dynamic_cast(expr_ty.get()); - return { ptr_ty->m_inner, false }; + auto ptr_ty = dynamic_cast(expr_ty.type.get()); + return { ptr_ty->m_inner, false, expr_ty.lvalue }; } void IndexAccessExpression::typecheck_preprocess(typecheck::Scope& scope) { @@ -365,27 +373,27 @@ namespace AST { typecheck::Scope& scope, std::optional> ) { - auto expr_ty = this->m_expr->typecheck(state, scope, {}).type; - if (expr_ty->m_kind != types::TypeKind::Pointer && expr_ty->m_kind != types::TypeKind::Array) { + auto expr_ty = this->m_expr->typecheck(state, scope, {}); + if (expr_ty.type->m_kind != types::TypeKind::Pointer && expr_ty.type->m_kind != types::TypeKind::Array) { 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 { new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, expr_ty.lvalue }; } - if (expr_ty->m_kind == types::TypeKind::Pointer) { - auto ptr_ty = dynamic_cast(expr_ty.get()); - return { ptr_ty->m_inner, false }; + if (expr_ty.type->m_kind == types::TypeKind::Pointer) { + auto ptr_ty = dynamic_cast(expr_ty.type.get()); + return { ptr_ty->m_inner, false, expr_ty.lvalue }; } - else if (expr_ty->m_kind == types::TypeKind::Array) { - auto ptr_ty = dynamic_cast(expr_ty.get()); - return { ptr_ty->m_inner, false }; + else if (expr_ty.type->m_kind == types::TypeKind::Array) { + auto ptr_ty = dynamic_cast(expr_ty.type.get()); + return { ptr_ty->m_inner, false, expr_ty.lvalue }; } // Default return type return { std::shared_ptr { new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, expr_ty.lvalue }; } void FieldAccessExpression::typecheck_preprocess(typecheck::Scope& scope) { @@ -397,32 +405,32 @@ namespace AST { typecheck::Scope& scope, std::optional> ) { - auto expr_ty = this->m_expr->typecheck(state, scope, {}).type; - if (expr_ty->m_kind != types::TypeKind::Struct) { + auto expr_ty = this->m_expr->typecheck(state, scope, {}); + if (expr_ty.type->m_kind != types::TypeKind::Struct) { 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 { new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, expr_ty.lvalue }; } - auto struct_ty = dynamic_cast(expr_ty.get()); + auto struct_ty = dynamic_cast(expr_ty.type.get()); if (struct_ty->m_fields) { for (auto& field : *struct_ty->m_fields) { 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)); return { std::shared_ptr { 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)); return { std::shared_ptr { new types::FundamentalType{ types::FundamentalTypeKind::Void } - }, false }; + }, false, expr_ty.lvalue }; } void ListInitializerExpression::typecheck_preprocess(typecheck::Scope& scope) { @@ -462,7 +470,7 @@ namespace AST { state.errors.push_back(CompileError( "Too many initializer values for " + struct_ty->formatted(), this->m_meta)); - return { *expected_ty, true }; + return { *expected_ty, true, false }; } for (int i = 0; i < static_cast(this->m_expressions.size()); i++) { @@ -473,18 +481,18 @@ namespace AST { } this->m_ty = *expected_ty; - return { this->m_ty, true }; + return { this->m_ty, true, false }; } else { if (this->m_expressions.size() > 0) { state.errors.push_back(CompileError( "Too many initializer values for " + struct_ty->formatted(), this->m_meta)); - return { *expected_ty, true }; + return { *expected_ty, true, false }; } else { this->m_ty = *expected_ty; - return { this->m_ty, true }; + return { this->m_ty, true, false }; } } @@ -492,10 +500,10 @@ namespace AST { else { return { std::shared_ptr { 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 @@ -508,7 +516,7 @@ namespace AST { true } }; - return { this->m_ty, true }; + return { this->m_ty, true, false }; } else { auto first_expr_ty = this->m_expressions[0]->typecheck(state, scope, {}).type; @@ -524,7 +532,7 @@ namespace AST { true } }; - return { this->m_ty, true }; + return { this->m_ty, true, false }; } } diff --git a/src/typechecker.h b/src/typechecker.h index 8ffdd7e..fab0cb5 100644 --- a/src/typechecker.h +++ b/src/typechecker.h @@ -24,6 +24,7 @@ namespace typecheck { struct ExpressionType { std::shared_ptr type; bool array_initializer; + bool lvalue; }; }