Skip to content

Commit

Permalink
[cpp] type system offers details about implicit/explicit type convers…
Browse files Browse the repository at this point in the history
…ions on-demand. might be useful for codegen. if its not, then you should just revert this and keep the code simpler
  • Loading branch information
harrand committed May 18, 2024
1 parent 65a683e commit 4b664fc
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 15 deletions.
8 changes: 4 additions & 4 deletions cpp/src/semal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ namespace semal
d.assert_that(std::holds_alternative<ast::identifier>(payload.rhs_expr->expr), std::format("in a cast, rhs of the cast token \"{}\" must be an identifier, not an expression or anything else.", payload.op.lexeme));
std::string type_name = std::get<ast::identifier>(payload.rhs_expr->expr).iden;
rhs = d.state.get_type_from_name(type_name);
d.assert_that(lhs.is_explicitly_convertible_to(rhs), std::format("cast from \"{}\" to \"{}\" is impossible", lhs.name(), rhs.name()));
d.assert_that(typecon_valid(lhs.is_explicitly_convertible_to(rhs)), std::format("cast from \"{}\" to \"{}\" is impossible", lhs.name(), rhs.name()));
d.assert_that(!rhs.is_undefined(), std::format("unknown cast destination type \"{}\"", type_name));
// todo: confirm that lhs can actually be casted to rhs.
}
Expand All @@ -560,7 +560,7 @@ namespace semal
{
case lex::type::operator_equals:
d.assert_that(!lhs.is_const(), std::format("lhs of assignment is const: \"{}\"", lhs.name()));
d.assert_that(rhs.is_implicitly_convertible_to(lhs), std::format("cannot assign from type \"{}\" to a type \"{}\"", rhs.name(), lhs.name()));
d.assert_that(typecon_valid(rhs.is_implicitly_convertible_to(lhs)), std::format("cannot assign from type \"{}\" to a type \"{}\"", rhs.name(), lhs.name()));
return rhs;
break;
case lex::type::operator_asterisk:
Expand Down Expand Up @@ -654,7 +654,7 @@ namespace semal
for(std::size_t i = 0; i < argc; i++)
{
type passed_ty = expression(d, *payload.params[i]);
d.assert_that(passed_ty.is_implicitly_convertible_to(maybe_function->params[i].ty), std::format("type mismatch to argument {} (\"{}\") - expected \"{}\", but you provided \"{}\". do you need an explicit cast?", i, maybe_function->params[i].name, maybe_function->params[i].ty.name(), passed_ty.name()));
d.assert_that(typecon_valid(passed_ty.is_implicitly_convertible_to(maybe_function->params[i].ty)), std::format("type mismatch to argument {} (\"{}\") - expected \"{}\", but you provided \"{}\". do you need an explicit cast?", i, maybe_function->params[i].name, maybe_function->params[i].ty.name(), passed_ty.name()));
}
return maybe_function->return_ty;
}
Expand Down Expand Up @@ -722,7 +722,7 @@ namespace semal
if(payload.initialiser.has_value())
{
type expr_ty = expression(d, *payload.initialiser.value());
d.assert_that(expr_ty.is_implicitly_convertible_to(ty), std::format("initialiser of variable \"{}\" is of type \"{}\", which is not implicitly convertible to the variable's type of \"{}\". perhaps you forgot to insert an explicit cast?", payload.var_name, expr_ty.name(), ty.name()));
d.assert_that(typecon_valid(expr_ty.is_implicitly_convertible_to(ty)), std::format("initialiser of variable \"{}\" is of type \"{}\", which is not implicitly convertible to the variable's type of \"{}\". perhaps you forgot to insert an explicit cast?", payload.var_name, expr_ty.name(), ty.name()));
}
return ty;
}
Expand Down
18 changes: 9 additions & 9 deletions cpp/src/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ bool type::is_weak() const
return this->qualifiers & qualifier_weak;
}

bool type::is_implicitly_convertible_to(const type& rhs) const
conversion_type type::is_implicitly_convertible_to(const type& rhs) const
{
if(*this == rhs)
{
return true;
return conversion_type::none;
}
// const T has the exact same rules as T
if(this->is_const())
Expand All @@ -176,33 +176,33 @@ bool type::is_implicitly_convertible_to(const type& rhs) const
if(plain_this == type::from_primitive(primitive_type::i64) && rhs.is_pointer())
{
// i64 -> any pointer (i.e uintptr_t)
return true;
return conversion_type::i2p;
}
if(this->is_integer_type() && rhs.is_integer_type())
{
// integer promotion.
return true;
return conversion_type::i2i;
}
if(this->is_floating_point_type() && rhs.is_floating_point_type())
{
// float promotion???
return true;
return conversion_type::f2f;
}
if(this->is_integer_type() && rhs.is_floating_point_type())
{
// integer -> floating point conversion.
return true;
return conversion_type::i2f;
}
if(this->is_pointer() && rhs.is_pointer())
{
// pointer -> pointer conversion.
return true;
return conversion_type::p2p;
}
}
return false;
return conversion_type::impossible;
}

bool type::is_explicitly_convertible_to(const type& rhs) const
conversion_type type::is_explicitly_convertible_to(const type& rhs) const
{
// you can explicitly convert (cast) to something if the weak variant of the current type is implicitly convertible.
auto weak_this = *this;
Expand Down
20 changes: 18 additions & 2 deletions cpp/src/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ enum type_qualifier

constexpr std::size_t array_size_dyn_array = std::numeric_limits<std::size_t>::max();

enum class conversion_type
{
none,
i2i,
f2f,
i2f,
p2p,
i2p,
impossible,
};

constexpr inline bool typecon_valid(const conversion_type& conv)
{
return conv != conversion_type::impossible;
}

struct type
{
std::variant
Expand All @@ -97,8 +113,8 @@ struct type
bool is_const() const;
bool is_weak() const;

bool is_implicitly_convertible_to(const type& rhs) const;
bool is_explicitly_convertible_to(const type& rhs) const;
conversion_type is_implicitly_convertible_to(const type& rhs) const;
conversion_type is_explicitly_convertible_to(const type& rhs) const;

primitive_type as_primitive() const;
struct_type as_struct() const;
Expand Down

0 comments on commit 4b664fc

Please sign in to comment.