Implement assignment binops a little differently
This commit is contained in:
parent
06231f466a
commit
0ea13482bf
@ -35,14 +35,14 @@ namespace types {
|
|||||||
bool_ty
|
bool_ty
|
||||||
}) {
|
}) {
|
||||||
definitions.push_back(BinopDefinition{
|
definitions.push_back(BinopDefinition{
|
||||||
ty, types::BinOp::Add, ty, ty,
|
ty, types::BinOp::Add, ty, ty, false,
|
||||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
return builder.builder->CreateAdd(lhs, rhs, "add");
|
return builder.builder->CreateAdd(lhs, rhs, "add");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
definitions.push_back(BinopDefinition{
|
definitions.push_back(BinopDefinition{
|
||||||
ty, types::BinOp::Sub, ty, ty,
|
ty, types::BinOp::Sub, ty, ty, false,
|
||||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
return builder.builder->CreateSub(lhs, rhs, "sub");
|
return builder.builder->CreateSub(lhs, rhs, "sub");
|
||||||
},
|
},
|
||||||
@ -54,14 +54,14 @@ namespace types {
|
|||||||
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
|
short_int_ty, int_ty, long_int_ty, long_long_int_ty, char_ty,
|
||||||
}) {
|
}) {
|
||||||
definitions.push_back(BinopDefinition{
|
definitions.push_back(BinopDefinition{
|
||||||
ty, types::BinOp::LessThan, ty, bool_ty,
|
ty, types::BinOp::LessThan, ty, bool_ty, false,
|
||||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt");
|
return builder.builder->CreateICmpSLT(lhs, rhs, "icmpslt");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
definitions.push_back(BinopDefinition{
|
definitions.push_back(BinopDefinition{
|
||||||
ty, types::BinOp::GreaterThan, ty, bool_ty,
|
ty, types::BinOp::GreaterThan, ty, bool_ty, false,
|
||||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
|
return builder.builder->CreateICmpSGT(lhs, rhs, "icmpsgt");
|
||||||
},
|
},
|
||||||
@ -74,14 +74,14 @@ namespace types {
|
|||||||
bool_ty
|
bool_ty
|
||||||
}) {
|
}) {
|
||||||
definitions.push_back(BinopDefinition{
|
definitions.push_back(BinopDefinition{
|
||||||
ty, types::BinOp::LessThan, ty, bool_ty,
|
ty, types::BinOp::LessThan, ty, bool_ty, false,
|
||||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
return builder.builder->CreateICmpULT(lhs, rhs, "icmpult");
|
return builder.builder->CreateICmpULT(lhs, rhs, "icmpult");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
definitions.push_back(BinopDefinition{
|
definitions.push_back(BinopDefinition{
|
||||||
ty, types::BinOp::GreaterThan, ty, bool_ty,
|
ty, types::BinOp::GreaterThan, ty, bool_ty, false,
|
||||||
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
return builder.builder->CreateICmpUGT(lhs, rhs, "icmpugt");
|
return builder.builder->CreateICmpUGT(lhs, rhs, "icmpugt");
|
||||||
},
|
},
|
||||||
@ -106,6 +106,20 @@ namespace types {
|
|||||||
|
|
||||||
return binop;
|
return binop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op == BinOp::Assignment) {
|
||||||
|
auto void_ty = std::shared_ptr<types::Type>{
|
||||||
|
new types::FundamentalType{ false, types::FundamentalTypeKind::Void } };;
|
||||||
|
|
||||||
|
return BinopDefinition{
|
||||||
|
lhs, types::BinOp::Assignment, lhs, void_ty, true,
|
||||||
|
[](codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs) {
|
||||||
|
builder.builder->CreateStore(rhs, lhs);
|
||||||
|
return llvm::dyn_cast<llvm::Value>(llvm::ConstantInt::get(llvm::IntegerType::get(*builder.context, 1), 0));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ namespace types {
|
|||||||
BinOp op;
|
BinOp op;
|
||||||
std::shared_ptr<Type> rhs;
|
std::shared_ptr<Type> rhs;
|
||||||
std::shared_ptr<Type> result;
|
std::shared_ptr<Type> result;
|
||||||
|
bool lhs_lvalue;
|
||||||
llvm::Value* (*codegen)(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
llvm::Value* (*codegen)(codegen::Builder& builder, llvm::Value* lhs, llvm::Value* rhs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -135,11 +135,6 @@ namespace AST {
|
|||||||
auto lhs = this->m_lhs->codegen(builder, this->m_binop == types::BinOp::Assignment ? lvalued : scope, allocator);
|
auto lhs = this->m_lhs->codegen(builder, this->m_binop == types::BinOp::Assignment ? lvalued : scope, allocator);
|
||||||
auto rhs = this->m_rhs->codegen(builder, scope, allocator);
|
auto rhs = this->m_rhs->codegen(builder, scope, allocator);
|
||||||
try {
|
try {
|
||||||
switch (this->m_binop) {
|
|
||||||
case types::BinOp::Assignment:
|
|
||||||
builder.builder->CreateStore(rhs.value, lhs.value, false);
|
|
||||||
return rhs;
|
|
||||||
default:
|
|
||||||
auto binop = types::find_binop(
|
auto binop = types::find_binop(
|
||||||
scope.binops,
|
scope.binops,
|
||||||
lhs.ty,
|
lhs.ty,
|
||||||
@ -153,7 +148,6 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
throw CompileError("invalid binop", this->m_meta);
|
throw CompileError("invalid binop", this->m_meta);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (std::runtime_error& error) {
|
catch (std::runtime_error& error) {
|
||||||
throw CompileError(error.what(), this->m_meta);
|
throw CompileError(error.what(), this->m_meta);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,21 +181,6 @@ namespace AST {
|
|||||||
auto lhs_ty = lhs_res.type;
|
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 (!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_const) {
|
|
||||||
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, false };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find a binop that matches exactly
|
// Try to find a binop that matches exactly
|
||||||
auto binop = types::find_binop(
|
auto binop = types::find_binop(
|
||||||
state.binops,
|
state.binops,
|
||||||
@ -205,6 +190,24 @@ namespace AST {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (binop) {
|
if (binop) {
|
||||||
|
if (binop->lhs_lvalue) {
|
||||||
|
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_const) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lhs_res = this->m_lhs->typecheck(state, scope, binop->lhs);
|
||||||
|
auto lhs_ty = lhs_res.type;
|
||||||
|
auto rhs_ty = this->m_rhs->typecheck(state, scope, binop->rhs).type;
|
||||||
|
|
||||||
|
auto lhs_ty_res = check_type(state, lhs_ty, binop->lhs);
|
||||||
|
auto rhs_ty_res = check_type(state, rhs_ty, binop->rhs);
|
||||||
|
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state);
|
||||||
|
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
|
||||||
|
|
||||||
return { binop->result, false, false };
|
return { binop->result, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +215,9 @@ namespace AST {
|
|||||||
// and is castable on the other side, and would also be perfectly
|
// and is castable on the other side, and would also be perfectly
|
||||||
// assignable to the expected value.
|
// assignable to the expected value.
|
||||||
for (auto& binop : state.binops) {
|
for (auto& binop : state.binops) {
|
||||||
|
if (binop.op != this->m_binop)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (expected_ty) {
|
if (expected_ty) {
|
||||||
// Skip any binops that would not be immediately assignable to
|
// Skip any binops that would not be immediately assignable to
|
||||||
// the expected type
|
// the expected type
|
||||||
@ -225,6 +231,15 @@ namespace AST {
|
|||||||
// Skip if not implicitly castable to lhs
|
// Skip if not implicitly castable to lhs
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (binop.lhs_lvalue) {
|
||||||
|
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_const) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
|
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
|
||||||
rhs_res = check_type(state, rhs_ty, binop.rhs);
|
rhs_res = check_type(state, rhs_ty, binop.rhs);
|
||||||
|
|
||||||
@ -232,14 +247,23 @@ namespace AST {
|
|||||||
return { binop.result, false, false };
|
return { binop.result, 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_ty_res = check_type(state, lhs_ty, binop.lhs);
|
||||||
if (!lhs_res.ok())
|
if (!lhs_ty_res.ok())
|
||||||
// Skip if not implicitly castable to rhs
|
// Skip if not implicitly castable to rhs
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (binop.lhs_lvalue) {
|
||||||
|
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_const) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
|
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
|
||||||
lhs_res = check_type(state, lhs_ty, binop.lhs);
|
lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
|
||||||
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
|
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state);
|
||||||
return { binop.result, false, false };
|
return { binop.result, false, false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,6 +271,9 @@ namespace AST {
|
|||||||
// if that fails, accept binops that match the result type perfectly and
|
// if that fails, accept binops that match the result type perfectly and
|
||||||
// is able to cast both types successfully
|
// is able to cast both types successfully
|
||||||
for (auto& binop : state.binops) {
|
for (auto& binop : state.binops) {
|
||||||
|
if (binop.op != this->m_binop)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (expected_ty) {
|
if (expected_ty) {
|
||||||
// Skip any binops that would not be immediately assignable to
|
// Skip any binops that would not be immediately assignable to
|
||||||
// the expected type
|
// the expected type
|
||||||
@ -255,23 +282,35 @@ namespace AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rhs_res = check_type(state, rhs_ty, binop.rhs);
|
auto rhs_ty_res = check_type(state, rhs_ty, binop.rhs);
|
||||||
auto lhs_res = check_type(state, lhs_ty, binop.lhs);
|
auto lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
|
||||||
if (!rhs_res.ok() || !lhs_res.ok())
|
if (!rhs_ty_res.ok() || !lhs_ty_res.ok())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (binop.lhs_lvalue) {
|
||||||
|
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_const) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
|
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
|
||||||
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
|
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
|
||||||
lhs_res = check_type(state, lhs_ty, binop.lhs);
|
lhs_ty_res = check_type(state, lhs_ty, binop.lhs);
|
||||||
rhs_res = check_type(state, rhs_ty, binop.rhs);
|
rhs_ty_res = check_type(state, rhs_ty, binop.rhs);
|
||||||
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_res, state);
|
this->m_lhs = handle_res(std::move(this->m_lhs), lhs_ty_res, state);
|
||||||
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_res, state);
|
this->m_rhs = handle_res(std::move(this->m_rhs), rhs_ty_res, state);
|
||||||
return { binop.result, false, false };
|
return { binop.result, false, false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally check for any binop that allows the result to be implicitly
|
// Finally check for any binop that allows the result to be implicitly
|
||||||
// casted to the result
|
// casted to the result
|
||||||
for (auto& binop : state.binops) {
|
for (auto& binop : state.binops) {
|
||||||
|
if (binop.op != this->m_binop)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (expected_ty) {
|
if (expected_ty) {
|
||||||
// Skip any binops that would not even be implicitly castable to
|
// Skip any binops that would not even be implicitly castable to
|
||||||
// the expected result
|
// the expected result
|
||||||
@ -279,6 +318,16 @@ namespace AST {
|
|||||||
if (!result_res.ok())
|
if (!result_res.ok())
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (binop.lhs_lvalue) {
|
||||||
|
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_const) {
|
||||||
|
state.errors.push_back(CompileError("Value must be a modifiable l-value", this->m_lhs->m_meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
|
lhs_ty = this->m_lhs->typecheck(state, scope, binop.lhs).type;
|
||||||
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
|
rhs_ty = this->m_rhs->typecheck(state, scope, binop.rhs).type;
|
||||||
auto lhs_result = check_type(state, lhs_ty, binop.lhs);
|
auto lhs_result = check_type(state, lhs_ty, binop.lhs);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user