summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/hir_typeck/expr_check.cpp17
-rw-r--r--src/hir_typeck/expr_cs.cpp32
-rw-r--r--src/mir/from_hir.cpp5
3 files changed, 52 insertions, 2 deletions
diff --git a/src/hir_typeck/expr_check.cpp b/src/hir_typeck/expr_check.cpp
index aa26ae1f..7abe5532 100644
--- a/src/hir_typeck/expr_check.cpp
+++ b/src/hir_typeck/expr_check.cpp
@@ -365,8 +365,21 @@ namespace {
default:
ERROR(sp, E0000, "Invalid cast to " << dst_ty << " from " << src_ty);
break;
- //TU_ARMA(Function, se) {
- // }
+ TU_ARMA(Function, se) {
+ if( se.is_unsafe != de.is_unsafe && se.is_unsafe )
+ ERROR(sp, E0000, "Invalid cast to " << dst_ty << " from " << src_ty << " - removing unsafe");
+ if( se.m_abi != de.m_abi )
+ ERROR(sp, E0000, "Invalid cast to " << dst_ty << " from " << src_ty << " - different ABI");
+ if( *se.m_rettype != *de.m_rettype )
+ ERROR(sp, E0000, "Invalid cast to " << dst_ty << " from " << src_ty << " - return type different");
+ if( se.m_arg_types.size() != de.m_arg_types.size() )
+ ERROR(sp, E0000, "Invalid cast to " << dst_ty << " from " << src_ty << " - argument count different");
+ for( size_t i = 0; i < se.m_arg_types.size(); i ++)
+ {
+ if( se.m_arg_types[i] != de.m_arg_types[i] )
+ ERROR(sp, E0000, "Invalid cast to " << dst_ty << " from " << src_ty << " - argument " << i << " different");
+ }
+ }
TU_ARMA(Closure, se) {
// Allowed, but won't exist after expansion
// TODO: Check argument types
diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp
index 6f6e7703..10cb9407 100644
--- a/src/hir_typeck/expr_cs.cpp
+++ b/src/hir_typeck/expr_cs.cpp
@@ -6549,6 +6549,38 @@ namespace {
return CoerceResult::Equality;
}
}
+ else if( const auto* se = src.m_data.opt_Function() )
+ {
+ if( const auto* de = dst.m_data.opt_Function() )
+ {
+ auto& node_ptr = *node_ptr_ptr;
+ auto span = node_ptr->span();
+ DEBUG("Function pointer coercion");
+ // ABI must match
+ if( se->m_abi != de->m_abi )
+ return CoerceResult::Equality;
+ // const can be removed
+ //if( se->is_const != de->is_const && de->is_const ) // Error going TO a const function pointer
+ // return CoerceResult::Equality;
+ // unsafe can be added
+ if( se->is_unsafe != de->is_unsafe && se->is_unsafe ) // Error going FROM an unsafe function pointer
+ return CoerceResult::Equality;
+ // argument/return types must match
+ if( de->m_arg_types.size() != se->m_arg_types.size() )
+ return CoerceResult::Equality;
+ for(size_t i = 0; i < de->m_arg_types.size(); i++)
+ {
+ context.equate_types(sp, de->m_arg_types[i], se->m_arg_types[i]);
+ }
+ context.equate_types(sp, *de->m_rettype, *se->m_rettype);
+ node_ptr = NEWNODE( dst.clone(), span, _Cast, mv$(node_ptr), dst.clone() );
+ return CoerceResult::Custom;
+ }
+ else
+ {
+ return CoerceResult::Equality;
+ }
+ }
else
{
// TODO: ! should be handled above or in caller?
diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp
index b8033a00..4ab1bf00 100644
--- a/src/mir/from_hir.cpp
+++ b/src/mir/from_hir.cpp
@@ -1279,6 +1279,11 @@ namespace {
TU_MATCH_HDRA( (ty_out.m_data), {)
default:
BUG(node.span(), "Invalid cast to " << ty_out << " from " << ty_in);
+ TU_ARMA(Function, de) {
+ // Just trust the previous stages.
+ ASSERT_BUG(node.span(), ty_in.m_data.is_Function(), ty_in);
+ ASSERT_BUG(node.span(), de.m_arg_types == ty_in.m_data.as_Function().m_arg_types, ty_in);
+ }
TU_ARMA(Pointer, de) {
if( ty_in.m_data.is_Primitive() ) {
const auto& ie = ty_in.m_data.as_Primitive();