Compare commits
5 Commits
1555c12bbd
...
57dc022241
| Author | SHA1 | Date | |
|---|---|---|---|
| 57dc022241 | |||
| 51c54e375a | |||
| 28483812ea | |||
| e0f2a1620e | |||
| ee0e30934a |
@ -44,7 +44,7 @@ namespace AST {
|
|||||||
|
|
||||||
std::string CastExpression::formatted() {
|
std::string CastExpression::formatted() {
|
||||||
std::stringstream out{ "" };
|
std::stringstream out{ "" };
|
||||||
out << "(" << this->m_ty->formatted() << ") ";
|
out << "(" << this->m_ty->formatted() << ")";
|
||||||
out << this->m_expr->formatted();
|
out << this->m_expr->formatted();
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
#include "casting.h"
|
#include "casting.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace types {
|
namespace types {
|
||||||
std::vector<CastDefinition> create_casts() {
|
std::vector<CastDefinition> create_casts() {
|
||||||
@ -21,15 +22,21 @@ namespace types {
|
|||||||
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
|
[](codegen::Builder&, std::shared_ptr<Type>, llvm::Value* value) {
|
||||||
return value;
|
return value;
|
||||||
} });
|
} });
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if (target_ty->is_signed()) {
|
|
||||||
casts.push_back(CastDefinition{ source_ty, target_ty, false,
|
bool allow_implicit = false;
|
||||||
|
if (target_ty->size() >= source_ty->size())
|
||||||
|
allow_implicit = true;
|
||||||
|
|
||||||
|
if (target_ty->is_signed()) {
|
||||||
|
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
|
||||||
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
|
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
|
||||||
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder), "cast");
|
return builder.builder->CreateSExtOrTrunc(value, target->codegen(builder), "cast");
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
casts.push_back(CastDefinition{ source_ty, target_ty, false,
|
casts.push_back(CastDefinition{ source_ty, target_ty, allow_implicit,
|
||||||
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
|
[](codegen::Builder& builder, std::shared_ptr<Type> target, llvm::Value* value) {
|
||||||
return builder.builder->CreateZExtOrTrunc(value, target->codegen(builder), "cast");
|
return builder.builder->CreateZExtOrTrunc(value, target->codegen(builder), "cast");
|
||||||
} });
|
} });
|
||||||
|
|||||||
@ -117,6 +117,8 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compile parsed output
|
||||||
|
|
||||||
codegen::Scope cg_scope{
|
codegen::Scope cg_scope{
|
||||||
.binops = typecheck_state.binops,
|
.binops = typecheck_state.binops,
|
||||||
.casts = typecheck_state.casts,
|
.casts = typecheck_state.casts,
|
||||||
@ -124,7 +126,6 @@ std::optional<CompileOutput> compile(std::string_view in_filename) {
|
|||||||
.is_lvalue = false,
|
.is_lvalue = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compile parsed output
|
|
||||||
try {
|
try {
|
||||||
for (auto& tls : statements) {
|
for (auto& tls : statements) {
|
||||||
std::cout << tls->formatted() << std::endl;
|
std::cout << tls->formatted() << std::endl;
|
||||||
|
|||||||
@ -5,14 +5,31 @@
|
|||||||
#include "result.h"
|
#include "result.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
enum class TypecheckRes {
|
enum class TypecheckResKind {
|
||||||
Ok,
|
Ok,
|
||||||
Castable,
|
Castable,
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<TypecheckRes, std::string> check_type(std::shared_ptr<types::Type> checked, std::shared_ptr<types::Type> target) {
|
struct TypecheckRes {
|
||||||
|
TypecheckResKind kind;
|
||||||
|
std::shared_ptr<types::Type> result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result<TypecheckRes, std::string> check_type(
|
||||||
|
typecheck::State& state,
|
||||||
|
std::shared_ptr<types::Type> checked,
|
||||||
|
std::shared_ptr<types::Type> target) {
|
||||||
|
auto potential_cast = types::find_cast(state.casts, checked, target);
|
||||||
|
|
||||||
|
|
||||||
if (types::types_equal(checked, target)) {
|
if (types::types_equal(checked, target)) {
|
||||||
return TypecheckRes::Ok;
|
return TypecheckRes{ TypecheckResKind::Ok, target };
|
||||||
|
}
|
||||||
|
else if (potential_cast.has_value()) {
|
||||||
|
if (potential_cast->allow_implicit)
|
||||||
|
return TypecheckRes{ TypecheckResKind::Castable, target };
|
||||||
|
|
||||||
|
return std::string{ "Type " + checked->formatted() + " not implicitly castable to " + target->formatted() };
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string{ "Types " + checked->formatted() + " and " + target->formatted() + " incompatible" };
|
return std::string{ "Types " + checked->formatted() + " and " + target->formatted() + " incompatible" };
|
||||||
@ -24,12 +41,13 @@ namespace {
|
|||||||
typecheck::State& state) {
|
typecheck::State& state) {
|
||||||
if (res.ok()) {
|
if (res.ok()) {
|
||||||
auto result = res.unwrap();
|
auto result = res.unwrap();
|
||||||
if (result == TypecheckRes::Ok) {
|
if (result.kind == TypecheckResKind::Ok) {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
state.errors.push_back(CompileError("Casting not yet implemented", expr->m_meta));
|
return std::unique_ptr<AST::Expression> {
|
||||||
return expr;
|
new AST::CastExpression{ expr->m_meta, result.result, std::move(expr) }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -93,7 +111,7 @@ namespace AST {
|
|||||||
std::shared_ptr<types::Type> BinaryOperationExpression::typecheck(
|
std::shared_ptr<types::Type> BinaryOperationExpression::typecheck(
|
||||||
typecheck::State& state,
|
typecheck::State& state,
|
||||||
typecheck::Scope& scope,
|
typecheck::Scope& scope,
|
||||||
std::optional<std::shared_ptr<types::Type>>
|
std::optional<std::shared_ptr<types::Type>> expected_ty
|
||||||
) {
|
) {
|
||||||
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
|
auto lhs_ty = this->m_lhs->typecheck(state, scope, {});
|
||||||
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
|
auto rhs_ty = this->m_rhs->typecheck(state, scope, {});
|
||||||
@ -101,9 +119,12 @@ namespace AST {
|
|||||||
if (this->m_binop == types::BinOp::Assignment) {
|
if (this->m_binop == types::BinOp::Assignment) {
|
||||||
// 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);
|
auto rhs_ty = this->m_rhs->typecheck(state, scope, 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);
|
||||||
return lhs_ty;
|
return lhs_ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to find a binop that matches exactly
|
||||||
auto binop = types::find_binop(
|
auto binop = types::find_binop(
|
||||||
state.binops,
|
state.binops,
|
||||||
lhs_ty,
|
lhs_ty,
|
||||||
@ -115,8 +136,53 @@ namespace AST {
|
|||||||
return binop->result;
|
return binop->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO check for binops that may be implicitly castable
|
// If that fails, try to find binop that matches on one side perfectly
|
||||||
|
// and is castable on the other side, and would also be perfectly
|
||||||
|
// assignable to the expected value.
|
||||||
|
for (auto& binop : state.binops) {
|
||||||
|
if (expected_ty) {
|
||||||
|
// Skip any binops that would not be immediately assignable to
|
||||||
|
// the expected type
|
||||||
|
if (!types::types_equal(binop.result, *expected_ty)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (types::types_equal(binop.lhs, lhs_ty)) {
|
||||||
|
auto rhs_res = check_type(state, rhs_ty, binop.rhs);
|
||||||
|
if (!rhs_res.ok())
|
||||||
|
// Skip if not implicitly castable to lhs
|
||||||
|
continue;
|
||||||
|
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
|
||||||
|
return binop.result;
|
||||||
|
}
|
||||||
|
else if (types::types_equal(binop.rhs, rhs_ty)) {
|
||||||
|
auto lhs_res = check_type(state, lhs_ty, binop.lhs);
|
||||||
|
if (!lhs_res.ok())
|
||||||
|
// Skip if not implicitly castable to rhs
|
||||||
|
continue;
|
||||||
|
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
|
||||||
|
return binop.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally check for any binop that allows the result to be implicitly
|
||||||
|
// casted to the result
|
||||||
|
for (auto& binop : state.binops) {
|
||||||
|
if (expected_ty) {
|
||||||
|
// Skip any binops that would not even be implicitly castable to
|
||||||
|
// the expected result
|
||||||
|
auto result_res = check_type(state, binop.result, *expected_ty);
|
||||||
|
if (!result_res.ok())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto lhs_result = check_type(state, lhs_ty, binop.lhs);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No suitable binops found :(
|
||||||
state.errors.push_back(CompileError(
|
state.errors.push_back(CompileError(
|
||||||
"No suitable binop between "
|
"No suitable binop between "
|
||||||
+ lhs_ty->formatted() + " "
|
+ lhs_ty->formatted() + " "
|
||||||
@ -156,7 +222,7 @@ namespace AST {
|
|||||||
auto expected_param_ty = fn_ty->m_param_tys[i];
|
auto expected_param_ty = fn_ty->m_param_tys[i];
|
||||||
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty);
|
auto param_ty = this->m_args[i]->typecheck(state, scope, expected_param_ty);
|
||||||
|
|
||||||
auto check_res = check_type(param_ty, expected_param_ty);
|
auto check_res = check_type(state, param_ty, expected_param_ty);
|
||||||
this->m_args[i] = handle_res(std::move(this->m_args[i]), check_res, state);
|
this->m_args[i] = handle_res(std::move(this->m_args[i]), check_res, state);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -190,7 +256,7 @@ namespace AST {
|
|||||||
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
void ReturnStatement::typecheck(typecheck::State& state, typecheck::Scope& scope) {
|
||||||
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
|
auto res_ty = this->m_expr->typecheck(state, scope, scope.return_ty);
|
||||||
if (scope.return_ty) {
|
if (scope.return_ty) {
|
||||||
auto check_res = check_type(res_ty, *scope.return_ty);
|
auto check_res = check_type(state, res_ty, *scope.return_ty);
|
||||||
this->m_expr = handle_res(std::move(this->m_expr), check_res, state);
|
this->m_expr = handle_res(std::move(this->m_expr), check_res, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,7 +277,7 @@ namespace AST {
|
|||||||
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
|
new types::FundamentalType{ types::FundamentalTypeKind::Bool } };
|
||||||
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
|
auto expr_ty = this->m_condition->typecheck(state, scope, bool_ty);
|
||||||
|
|
||||||
auto check_res = check_type(expr_ty, bool_ty);
|
auto check_res = check_type(state, expr_ty, bool_ty);
|
||||||
this->m_condition = handle_res(std::move(this->m_condition), check_res, state);
|
this->m_condition = handle_res(std::move(this->m_condition), check_res, state);
|
||||||
|
|
||||||
this->m_then->typecheck(state, scope);
|
this->m_then->typecheck(state, scope);
|
||||||
|
|||||||
@ -76,7 +76,7 @@ namespace types {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Type::is_signed() {
|
bool Type::is_signed() {
|
||||||
false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr) {
|
std::pair<llvm::Value*, std::shared_ptr<Type>> FundamentalType::load(codegen::Builder&, llvm::Value* ptr) {
|
||||||
@ -140,6 +140,19 @@ namespace types {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t FundamentalType::size() {
|
||||||
|
switch (this->m_ty) {
|
||||||
|
case FundamentalTypeKind::Int:
|
||||||
|
return 32;
|
||||||
|
case FundamentalTypeKind::Bool:
|
||||||
|
return 1;
|
||||||
|
case FundamentalTypeKind::Char:
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string FunctionType::formatted() {
|
std::string FunctionType::formatted() {
|
||||||
std::stringstream out{ "" };
|
std::stringstream out{ "" };
|
||||||
out << "(";
|
out << "(";
|
||||||
@ -170,6 +183,10 @@ namespace types {
|
|||||||
return std::pair(ptr, self);
|
return std::pair(ptr, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t FunctionType::size() {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
std::string PointerType::formatted() {
|
std::string PointerType::formatted() {
|
||||||
std::stringstream out{ "" };
|
std::stringstream out{ "" };
|
||||||
out << this->m_inner->formatted() << "*";
|
out << this->m_inner->formatted() << "*";
|
||||||
@ -183,6 +200,10 @@ namespace types {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t PointerType::size() {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2) {
|
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2) {
|
||||||
if (type1->m_kind != type2->m_kind)
|
if (type1->m_kind != type2->m_kind)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -34,6 +34,7 @@ namespace types {
|
|||||||
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
||||||
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
||||||
virtual bool is_signed();
|
virtual bool is_signed();
|
||||||
|
virtual uint32_t size() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FundamentalType : public Type {
|
class FundamentalType : public Type {
|
||||||
@ -49,6 +50,7 @@ namespace types {
|
|||||||
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
|
virtual llvm::Value* lt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
|
||||||
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
|
virtual llvm::Value* gt(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) override;
|
||||||
virtual bool is_signed() override;
|
virtual bool is_signed() override;
|
||||||
|
virtual uint32_t size() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ namespace types {
|
|||||||
virtual llvm::Type* codegen(codegen::Builder& builder) override;
|
virtual llvm::Type* codegen(codegen::Builder& builder) override;
|
||||||
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
|
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
|
||||||
virtual std::optional<std::shared_ptr<Type>> return_type() override;
|
virtual std::optional<std::shared_ptr<Type>> return_type() override;
|
||||||
|
virtual uint32_t size() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +85,7 @@ namespace types {
|
|||||||
virtual std::string formatted() override;
|
virtual std::string formatted() override;
|
||||||
virtual llvm::Type* codegen(codegen::Builder& builder) override;
|
virtual llvm::Type* codegen(codegen::Builder& builder) override;
|
||||||
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
|
virtual std::pair<llvm::Value*, std::shared_ptr<Type>> load(codegen::Builder& builder, llvm::Value* ptr) override;
|
||||||
|
virtual uint32_t size() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2);
|
bool types_equal(std::shared_ptr<types::Type> type1, std::shared_ptr<types::Type> type2);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user