From 4b664fc190ef7d26866fd04e70b51e969d757b6d Mon Sep 17 00:00:00 2001 From: Harrand Date: Sat, 18 May 2024 16:34:12 +0100 Subject: [PATCH] [cpp] type system offers details about implicit/explicit type conversions on-demand. might be useful for codegen. if its not, then you should just revert this and keep the code simpler --- cpp/src/semal.cpp | 8 ++++---- cpp/src/type.cpp | 18 +++++++++--------- cpp/src/type.hpp | 20 ++++++++++++++++++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/cpp/src/semal.cpp b/cpp/src/semal.cpp index d297c90..d7423f5 100644 --- a/cpp/src/semal.cpp +++ b/cpp/src/semal.cpp @@ -548,7 +548,7 @@ namespace semal d.assert_that(std::holds_alternative(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(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. } @@ -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: @@ -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; } @@ -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; } diff --git a/cpp/src/type.cpp b/cpp/src/type.cpp index 8b1c09d..9f8a447 100644 --- a/cpp/src/type.cpp +++ b/cpp/src/type.cpp @@ -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()) @@ -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; diff --git a/cpp/src/type.hpp b/cpp/src/type.hpp index 6141066..e6c71f4 100644 --- a/cpp/src/type.hpp +++ b/cpp/src/type.hpp @@ -71,6 +71,22 @@ enum type_qualifier constexpr std::size_t array_size_dyn_array = std::numeric_limits::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 @@ -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;