summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2016-08-13 16:15:15 +0800
committerJohn Hodge <tpg@mutabah.net>2016-08-13 16:15:15 +0800
commit51973a64fe30d9481eabe9456982ab50542116f0 (patch)
treea7f86c13437d4b57c2dde413150d92a78d529b31 /src
parentb8a79dcc476ad85f0c7c0fcf646fcdb4bb357219 (diff)
downloadmrust-51973a64fe30d9481eabe9456982ab50542116f0.tar.gz
HIR Expand - Replace assign and boolean operator overloads with calls
Diffstat (limited to 'src')
-rw-r--r--src/hir_expand/ufcs_everything.cpp282
1 files changed, 280 insertions, 2 deletions
diff --git a/src/hir_expand/ufcs_everything.cpp b/src/hir_expand/ufcs_everything.cpp
index 17168fbd..ff45f8d2 100644
--- a/src/hir_expand/ufcs_everything.cpp
+++ b/src/hir_expand/ufcs_everything.cpp
@@ -3,7 +3,7 @@
* - By John Hodge (Mutabah/thePowersGang)
*
* hir_expand/ufcs_everything.cpp
- * - HIR Expansion - All calls (methods, values, ...) as UFCS path calls
+ * - Expand all function calls (_CallMethod, and _CallValue) and operator overloads to _CallPath
*/
#include <hir/visitor.hpp>
#include <hir/expr.hpp>
@@ -50,7 +50,10 @@ namespace {
}
}
-
+ // ----------
+ // _CallValue
+ // ----------
+ // Replace with a UFCS call using the now-known type
void visit(::HIR::ExprNode_CallValue& node) override
{
const auto& sp = node.span();
@@ -136,11 +139,17 @@ namespace {
mv$(args)
);
+ // Populate the cache for later passes
auto& arg_types = dynamic_cast< ::HIR::ExprNode_CallPath&>(*m_replacement).m_cache.m_arg_types;
arg_types.push_back( mv$(self_arg_type) );
for(auto& ty : node.m_arg_types)
arg_types.push_back( mv$(ty) );
}
+
+ // ----------
+ // _CallMethod
+ // ----------
+ // Simple replacement
void visit(::HIR::ExprNode_CallMethod& node) override
{
const auto& sp = node.span();
@@ -158,8 +167,277 @@ namespace {
mv$(node.m_method_path),
mv$(args)
);
+ // Populate the cache for later passes
dynamic_cast< ::HIR::ExprNode_CallPath&>(*m_replacement).m_cache = mv$(node.m_cache);
}
+
+
+ static bool is_op_valid_shift(const ::HIR::TypeRef& ty_l, const ::HIR::TypeRef& ty_r)
+ {
+ // Integer with any other integer is valid, others go to overload resolution
+ if( ty_l.m_data.is_Primitive() && ty_r.m_data.is_Primitive() ) {
+ switch(ty_l.m_data.as_Primitive())
+ {
+ case ::HIR::CoreType::Char:
+ case ::HIR::CoreType::Str:
+ case ::HIR::CoreType::Bool:
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64:
+ break;
+ default:
+ switch(ty_r.m_data.as_Primitive())
+ {
+ case ::HIR::CoreType::Char:
+ case ::HIR::CoreType::Str:
+ case ::HIR::CoreType::Bool:
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64:
+ break;
+ default:
+ // RETURN early
+ return true;
+ }
+ break;
+ }
+
+ }
+ return false;
+ }
+ static bool is_op_valid_bitmask(const ::HIR::TypeRef& ty_l, const ::HIR::TypeRef& ty_r)
+ {
+ // Equal integers and bool are valid
+ if( ty_l == ty_r ) {
+ TU_IFLET(::HIR::TypeRef::Data, ty_l.m_data, Primitive, e,
+ switch(e)
+ {
+ case ::HIR::CoreType::Char:
+ case ::HIR::CoreType::Str:
+ break;
+ default:
+ // RETURN early
+ return true;
+ }
+ )
+ }
+ return false;
+ }
+ static bool is_op_valid_arith(const ::HIR::TypeRef& ty_l, const ::HIR::TypeRef& ty_r)
+ {
+ // Equal floats/integers are valid, others go to overload
+ if( ty_l == ty_r ) {
+ TU_IFLET(::HIR::TypeRef::Data, ty_l.m_data, Primitive, e,
+ switch(e)
+ {
+ case ::HIR::CoreType::Char:
+ case ::HIR::CoreType::Str:
+ case ::HIR::CoreType::Bool:
+ break;
+ default:
+ // RETURN early
+ return true;
+ }
+ )
+ }
+ return false;
+ }
+
+ // -------
+ // _Assign
+ // -------
+ // Replace with overload call if not a builtin supported operation
+ void visit(::HIR::ExprNode_Assign& node) override
+ {
+ const auto& sp = node.span();
+ ::HIR::ExprVisitorDef::visit(node);
+
+ const auto& ty_slot = node.m_slot->m_res_type;
+ const auto& ty_val = node.m_value->m_res_type;
+
+ const char* langitem = nullptr;
+ const char* opname = nullptr;
+ #define _(opname) case ::HIR::ExprNode_Assign::Op::opname
+ switch( node.m_op )
+ {
+ _(None):
+ ASSERT_BUG(sp, ty_slot == ty_val, "Types must equal for non-operator assignment, " << ty_slot << " != " << ty_val);
+ return ;
+ _(Shr): {langitem = "shr_assign"; opname = "shr_assign"; } if(0)
+ _(Shl): {langitem = "shl_assign"; opname = "shl_assign"; } if(0)
+ ;
+ if( is_op_valid_shift(ty_slot, ty_val) ) {
+ return ;
+ }
+ break;
+
+ _(And): {langitem = "bitand_assign"; opname = "bitand_assign"; } if(0)
+ _(Or ): {langitem = "bitor_assign" ; opname = "bitor_assign" ; } if(0)
+ _(Xor): {langitem = "bitxor_assign"; opname = "bitxor_assign"; } if(0)
+ ;
+ if( is_op_valid_bitmask(ty_slot, ty_val) ) {
+ return ;
+ }
+ break;
+
+ _(Add): {langitem = "add_assign"; opname = "add_assign"; } if(0)
+ _(Sub): {langitem = "sub_assign"; opname = "sub_assign"; } if(0)
+ _(Mul): {langitem = "mul_assign"; opname = "mul_assign"; } if(0)
+ _(Div): {langitem = "div_assign"; opname = "div_assign"; } if(0)
+ _(Mod): {langitem = "rem_assign"; opname = "rem_assign"; } if(0)
+ ;
+ if( is_op_valid_arith(ty_slot, ty_val) ) {
+ return ;
+ }
+ // - Fall down to overload replacement
+ break;
+ }
+ #undef _
+ assert( langitem );
+ assert( opname );
+
+ // Needs replacement, continue
+ ::HIR::PathParams trait_params;
+ trait_params.m_types.push_back( ty_val.clone() );
+ ::HIR::GenericPath trait { m_crate.get_lang_item_path(node.span(), langitem), mv$(trait_params) };
+
+ auto slot_type_refmut = ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Unique, ty_slot.clone());
+ ::std::vector< ::HIR::ExprNodeP> args;
+ args.push_back( NEWNODE( slot_type_refmut.clone(), UniOp, sp,
+ ::HIR::ExprNode_UniOp::Op::RefMut, mv$(node.m_slot)
+ ) );
+ args.push_back( mv$(node.m_value) );
+ m_replacement = NEWNODE(mv$(node.m_res_type), CallPath, sp,
+ ::HIR::Path(ty_slot.clone(), mv$(trait), opname),
+ mv$(args)
+ );
+
+ // Populate the cache for later passes
+ auto& arg_types = dynamic_cast< ::HIR::ExprNode_CallPath&>(*m_replacement).m_cache.m_arg_types;
+ arg_types.push_back( mv$(slot_type_refmut) );
+ arg_types.push_back( ty_val.clone() );
+ arg_types.push_back( ::HIR::TypeRef::new_unit() );
+ }
+
+ void visit(::HIR::ExprNode_BinOp& node) override
+ {
+ const auto& sp = node.span();
+ ::HIR::ExprVisitorDef::visit(node);
+
+ const auto& ty_l = node.m_left->m_res_type;
+ const auto& ty_r = node.m_right->m_res_type;
+
+ const char* langitem = nullptr;
+ const char* method = nullptr;
+ switch(node.m_op)
+ {
+ case ::HIR::ExprNode_BinOp::Op::CmpEqu: { langitem = "eq"; method = "eq"; } if(0)
+ case ::HIR::ExprNode_BinOp::Op::CmpNEqu:{ langitem = "eq"; method = "ne"; } if(0)
+ case ::HIR::ExprNode_BinOp::Op::CmpLt: { langitem = "ord"; method = "lt"; } if(0)
+ case ::HIR::ExprNode_BinOp::Op::CmpLtE: { langitem = "ord"; method = "le"; } if(0)
+ case ::HIR::ExprNode_BinOp::Op::CmpGt: { langitem = "ord"; method = "gt"; } if(0)
+ case ::HIR::ExprNode_BinOp::Op::CmpGtE: { langitem = "ord"; method = "ge"; } if(0)
+ ; {
+ // 1. Check if the types are valid for primitive comparison
+ if( ty_l == ty_r ) {
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty_l.m_data), (e),
+ (
+ // Unknown - Overload
+ ),
+ (Pointer,
+ // Raw pointer, valid.
+ return ;
+ ),
+ // TODO: Should comparing &str be handled by the overload, or MIR?
+ (Primitive,
+ if( e != ::HIR::CoreType::Str ) {
+ return ;
+ }
+ )
+ )
+ }
+ // 2. If not, emit a call with params borrowed
+ ::HIR::PathParams trait_params;
+ trait_params.m_types.push_back( ty_r.clone() );
+ ::HIR::GenericPath trait { m_crate.get_lang_item_path(node.span(), langitem), mv$(trait_params) };
+
+ auto ty_l_ref = ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ty_l.clone() );
+ auto ty_r_ref = ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ty_r.clone() );
+
+ ::std::vector< ::HIR::ExprNodeP> args;
+ args.push_back( NEWNODE(ty_l_ref.clone(), UniOp, node.m_left->span(),
+ ::HIR::ExprNode_UniOp::Op::Ref, mv$(node.m_left)
+ ) );
+ args.push_back( NEWNODE(ty_r_ref.clone(), UniOp, node.m_right->span(),
+ ::HIR::ExprNode_UniOp::Op::Ref, mv$(node.m_right)
+ ) );
+
+ m_replacement = NEWNODE(mv$(node.m_res_type), CallPath, sp,
+ ::HIR::Path(ty_l.clone(), mv$(trait), method),
+ mv$(args)
+ );
+
+ // Populate the cache for later passes
+ auto& arg_types = dynamic_cast< ::HIR::ExprNode_CallPath&>(*m_replacement).m_cache.m_arg_types;
+ arg_types.push_back( mv$(ty_l_ref) );
+ arg_types.push_back( mv$(ty_r_ref) );
+ arg_types.push_back( ::HIR::TypeRef( ::HIR::CoreType::Bool ) );
+ return ;
+ } break;
+
+ case ::HIR::ExprNode_BinOp::Op::Xor: langitem = method = "bitxor"; if(0)
+ case ::HIR::ExprNode_BinOp::Op::Or : langitem = method = "bitor" ; if(0)
+ case ::HIR::ExprNode_BinOp::Op::And: langitem = method = "bitand"; if(0)
+ ;
+ if( is_op_valid_bitmask(ty_l, ty_r) ) {
+ return ;
+ }
+ break;
+
+ case ::HIR::ExprNode_BinOp::Op::Shr: langitem = method = "shr"; if(0)
+ case ::HIR::ExprNode_BinOp::Op::Shl: langitem = method = "shr";
+ if( is_op_valid_shift(ty_l, ty_r) ) {
+ return ;
+ }
+ break;
+
+ case ::HIR::ExprNode_BinOp::Op::Add: langitem = method = "add"; if(0)
+ case ::HIR::ExprNode_BinOp::Op::Sub: langitem = method = "sub"; if(0)
+ case ::HIR::ExprNode_BinOp::Op::Mul: langitem = method = "mul"; if(0)
+ case ::HIR::ExprNode_BinOp::Op::Div: langitem = method = "div"; if(0)
+ case ::HIR::ExprNode_BinOp::Op::Mod: langitem = method = "rem";
+ if( is_op_valid_arith(ty_l, ty_r) ) {
+ return ;
+ }
+ break;
+
+ case ::HIR::ExprNode_BinOp::Op::BoolAnd:
+ case ::HIR::ExprNode_BinOp::Op::BoolOr:
+ ASSERT_BUG(sp, ty_l == ::HIR::TypeRef(::HIR::CoreType::Bool), "&& operator requires bool");
+ ASSERT_BUG(sp, ty_r == ::HIR::TypeRef(::HIR::CoreType::Bool), "&& operator requires bool");
+ return ;
+ }
+ assert(langitem);
+ assert(method);
+
+ // Needs replacement, continue
+ ::HIR::PathParams trait_params;
+ trait_params.m_types.push_back( ty_r.clone() );
+ ::HIR::GenericPath trait { m_crate.get_lang_item_path(node.span(), langitem), mv$(trait_params) };
+
+ ::std::vector< ::HIR::ExprNodeP> args;
+ args.push_back( mv$(node.m_left) );
+ args.push_back( mv$(node.m_right) );
+
+ m_replacement = NEWNODE(mv$(node.m_res_type), CallPath, sp,
+ ::HIR::Path(ty_l.clone(), mv$(trait), method),
+ mv$(args)
+ );
+
+ // Populate the cache for later passes
+ auto& arg_types = dynamic_cast< ::HIR::ExprNode_CallPath&>(*m_replacement).m_cache.m_arg_types;
+ arg_types.push_back( ty_l.clone() );
+ arg_types.push_back( ty_r.clone() );
+ arg_types.push_back( m_replacement->m_res_type.clone() );
+ }
};
class OuterVisitor:
public ::HIR::Visitor